00001 // Welcome to the official first version of the OpenEm M6502-variant emulator. 00002 // This emulator was written with portability and ease of use in mind. 00003 00004 // It was originally meant just to host two varieties: M6502 and M65C02. However, it was decided 00005 // that it made sense to expand it to cover the ENTIRE 6500 family including 65816. 00006 00007 // The overall structure is based around a jump table: When an instruction is read, we jump 00008 // from a table of function pointers immediatly to its emulating function. Thus, there is 00009 // no need for up to 255 compares to find which opcode we're using and jump to it, nor is 00010 // there a need for expensive binary decoding. This is about as close to dynarec or static 00011 // translation speed as you can get in an interperater without using a state machine. 00012 // Unfortunately, this approach is only useable on older processors: variable instruction 00013 // lengths can be handled with multiple jump tables, but as soon as you start introducing 16-bit 00014 // instructions, we're talking about 1meg+ jump tables that have no hope of ever fitting in 00015 // cache. In such a situation, it is far faster to simply bit-decode the instructions. (Unless, 00016 // of course, you're working on something like an ARM processor where memory speed matches 00017 // processor speed. But this is relatively very rae.) 00018 00019 // When you first call the M65C02's init() function, it just zeroes things. Then, when you 00020 // call its set_mode() function, it fills in the jump table based on what processor you're 00021 // emulating. 00022 00023 // If you actually use this in an emulator, it is advisable to modify it slightly to your 00024 // needs. For instance, for an Atari 2600 emulator, you may wish to add in the HALT-TIL-END- 00025 // OF-SCANLINE functionality. 00026 00027 // This code is released under the LGPL. You can find what that means in the file 'LICENSE' 00028 // in the root directory of the LGPL. 00029 00030 #ifndef __CPU_M65C02_H__ 00031 #define __CPU_M65C02_H__ 00032 00033 #include "mempage.h" 00034 #include "cpu/cycle.h" 00035 00036 #define M65C02_MODE_M6502 0 // Regular old M6502 00037 #define M65C02_MODE_M65C02 1 // M65C02. More opcodes, mostly. Used in commodore64, etc. 00038 #define M65C02_MODE_M65C02E 2 // Rockwell and CDP, more instructions 00039 #define M65C02_MODE_M6507 3 // Atari 2600 processor. Only 13 external address lines. For the purpose of speed, all 16 address lines are emulated as in a normal M6502. It's up to you to ignore the top 3 in your emulator. 00040 #define M65C02_MODE_M6508 4 // 8-bit I/O port, and 256 bytes built-in RAM. Both of which are emulated externally. See docs for more info. 00041 #define M65C02_MODE_M6509 5 // Built-in bank switching for up to 1mb of RAM. Other than that, a generic 6502. 00042 #define M65C02_MODE_M6510 6 // Regular M6502, with built-in 8-bit I/O port - supports undocumented opcodes 00043 #define M65C02_MODE_M6510T 7 // Same as M6510, no NMI/RDY inputs 00044 #define M65C02_MODE_SALLY 8 // Regular M6502, but with a HALT input. All but the earliest Atari 8-bit computers used this. 00045 #define M65C02_MODE_M6501 M65C02_MODE_M6502 00046 #define M65C02_MODE_2A03 9 // The NES's variant of the M6502. No Decimal mode, and memory mapped registers (which are emulated externally). 00047 #define M65C02_MODE_M65816 10 // 16-bit "upgrade". Boots up in M6502 emulation mode, has 16-bit mode. 00048 00049 // State of the processor. Everything it needs to resume where it was. 00050 struct CPU_M65C02_STATE { 00051 byte X; // X index register 00052 byte Y; // Y index register 00053 byte A; // Accumulator 00054 byte S; // Status register 00055 word PC; // Program counter 00056 byte SP; // Stack pointer 00057 word procmode; // Processor mode 00058 //dword cyclsleft; // Cycles left 00059 uint32 NMI; // NMI 00060 uint32 IRQ; // IRQ 00061 uint32 HLT; // HLT 00062 CPU_CYCLE_COUNT cycles; 00063 }; 00064 00065 // Struct that holds the information for each instruction, such as timing, etc. 00066 struct CPU_M65C02_ins { 00067 int amode; // Addressing mode 00068 int cycl; // # of cycles 00069 int size; // Size of the instruction including addressing, in bytes 00070 int badd; // How many cycles this will take extra if it branches 00071 int pbadd; // How many cycles this will take extra of the branch crosses a page boundary 00072 }; 00073 00074 struct CPU_M65C02_DEBUG_STATE { 00075 uint32 flag; 00076 byte nextmem[3]; 00077 CPU_M65C02_STATE s; 00078 }; 00079 00080 class CPU_M65C02 { 00081 public: 00082 void init(); // 1) Call this on init 00083 void set_mode(int mode, int support); // 2) Then call this to set the mode. Arguments 1: M65C02_MODE_xxxx for processor type. Argument 2 should be 0 to turn off nonstandard (undocumented) opcode support, anything else to turn it on. 00084 // 3) Make sure your memory is setup before you do step 4! 00085 void reset(); // 4) Then call this to start the emulator. It will jump to the location contained at RESETVEC (0xFFFC) 00086 // Note that you MUST do all this before you use the processor, even restoring a state! 00087 00088 CPU_CYCLE_COUNT cycles; 00089 void cycle(dword numleft); // Do cycles! 00090 00091 void save_state(CPU_M65C02_STATE *foo); // Save the state! 00092 void restore_state(CPU_M65C02_STATE *foo); // Restore the state! 00093 00094 uint32 IRQ; // IRQ logic 00095 uint32 NMI; // NMI logic 00096 uint32 HLT; // Halt logic. Not implemented. 00097 00098 // Debug support 00099 void set_debug_mode(uint16 mode); // Set the debug mode! 00100 char *debug_str(CPU_M65C02_DEBUG_STATE *s); // Returns a string containing debug info dump 00101 CPU_DBGINF dbg; 00102 CPU_M65C02_DEBUG_STATE dbg_iop_state; // Holds the debug state on illegal op 00103 00104 // Memory system 00105 oe_pagetable mem; 00106 00107 private: 00108 word atabl[256]; 00109 00110 // void debugme(char *p); // Debug output 00111 byte curins; // internal use (current instruction) 00112 00113 //dword cyclovr; // This variable holds how many cycles are remaining on the current instruction 00114 00115 byte X; // X index register 00116 byte Y; // Y index register 00117 byte A; // Accumulator 00118 byte S; // Status register 00119 word PC; // Program counter 00120 byte SP; // Stack pointer 00121 00122 int pb; // Page boundary cross notifier 00123 int pba; // Page boundary cross adder 00124 int r; // If this is TRUE, the PC has been modified by the instruction and should not be touched by the main loop 00125 int pmode; // Processor mode 00126 int bra; // Branch add cycles # 00127 00128 void setjumptable(int ins, int mode); // Sets instruction jump table info 00129 CPU_M65C02_ins iinf[2][256]; // Various timing, addressing, etc. information for the instructions 00130 00131 void (CPU_M65C02::*inst[2][256])(void); // Instruction jump table 00132 00133 dword mem_addr(int mode); // Memory addressing thingy 00134 00135 int procmode; // The processor mode 00136 00137 int support_undoc; // Do we support undocumented opcodes?. NOT DONE YET 00138 int support_M65C; // Do we support M65C02 opcodes? NOT DONE YET 00139 int support_M65CE; // 65C02 extended instructions 00140 int support_M658; // Do we support 16-bit M65816 opcodes? NOT DONE YET 00141 int support_HLT; // Do we support the HLT instruction (SALLY)? NOT DONE YET 00142 int support_dec; // Do we support decimal mode? NOT DONE YET 00143 int decmode; // Is decimal mode currently set? 00144 int support_irq; // Do we support IRQs? IRQS WORK, FLAG NOT 00145 int support_nmi; // Do we support NMIs? NMIS WORK, FLAG NOT 00146 int support_banks; // Do we support 6509 bankswitching? NOT DONE YET 00147 int support_indjumpbug; // Do we support the indirect jump bug? DONE 00148 int jumpbugmode; // What is the jump bug mode? 00149 00150 // Vectors 00151 dword IRQVEC; // IRQ 00152 dword RESETVEC; // RESET 00153 dword NMIVEC; // NMI 00154 dword ABORTVEC; // ABORT 00155 dword COPVEC; // COP 00156 00157 // Debug stuff 00158 void IllegalOpcode(); // Illegal operation. 00159 00160 // Regular M6502 Instructions 00161 void ADC(); // Add memory to accumulator with carry 00162 void AND(); // And memory with accumulator 00163 void ASL(); // Shift left one bit (memory or accumulator) 00164 void ASLA(); // Arithmetic shift left A-variant 00165 void BCC(); // Branch on carry clear 00166 void BCS(); // Branch on carry set 00167 void BEQ(); // Branch on result 0 00168 void BIT(); // Test bits in memory with accumulator 00169 void BMI(); // Branch on result minus 00170 void BNE(); // Branch on result not zero 00171 void BPL(); // Branch on result plus 00172 void BRK(); // Force break 00173 void BVC(); // Branch on overflow clear 00174 void BVS(); // Branch on overflow set 00175 void CLC(); // Clear carry flag 00176 void CLD(); // Clear decimal mode flag 00177 void CLI(); // Clear interrupt disable bit 00178 void CLV(); // Clear overflow flag 00179 void CMP(); // Compare memory and accumulator 00180 void CPX(); // Compare memory and Index X 00181 void CPY(); // Compare memory and Index Y 00182 void DEC(); // Decrement memory by 1 00183 void DEX(); // Decrement X by 1 00184 void DEY(); // Decrement Y by 1 00185 void EOR(); // XOR memory with accumulator 00186 void INC(); // Increment memory by 1 00187 void INX(); // Increment X by 1 00188 void INY(); // Increment Y by 1 00189 void JMP(); // Jump to new location 00190 void JSR(); // Jump to new location saving return address 00191 void LDA(); // Load accumulator with memory 00192 void LDX(); // Load X with memory 00193 void LDY(); // Load Y with memory 00194 void LSR(); // Shift right one bit accumulator or memory 00195 void LSRA(); // Shift right one bit A 00196 void NOP(); // No operation 00197 void ORA(); // OR memory with accumulator 00198 void PHA(); // Push A onto stack 00199 void PHP(); // Push S onto stack 00200 void PLA(); // Pop A 00201 void PLP(); // Pop P 00202 void ROL(); // Rotate one bit left (A or mem) 00203 void ROLA(); // Rotate one bit left (A) 00204 void ROR(); // Rotate one bit right (A or mem) 00205 void RORA(); // Rotate one bit right (A) 00206 void RTI(); // Return from interrupt 00207 void RTS(); // Return from subroutine 00208 void SBC(); // Subtract memory from accumulator with borrow 00209 void SEC(); // Set Carry Flag 00210 void SED(); // Set Decimal Mode 00211 void SEI(); // Set Interrupt Disable status 00212 void STA(); // Store Accumulator in memory 00213 void STX(); // Store X in mem 00214 void STY(); // Store Y in mem 00215 void TAX(); // Transfer A to X 00216 void TAY(); // Transfer A to Y 00217 void TSX(); // Transfer SP(Stack Pointer) to X 00218 void TXA(); // Transfer X to A 00219 void TXS(); // Transfer X to SP 00220 void TYA(); // Transfer Y to A 00221 00222 // Undocumented opcodes 00223 void AAC(void); // AAC AND byte with accumulator, set carry by N 00224 void AAX(void); // AAX AND X with accumulator, set memory 00225 void ARR(void); //*ARR AND byte with accumulator, local rotate accumulator 1 bit right, set V/C complexly 00226 void ASR(void); // ASR AND byte with accumulator, then shift A right 1 00227 void ATX(void); // ATX AND byte with accumulator, and TXA 00228 void AXA(void); // AXA AND X with accumulator, then AND with 7, then store in memory 00229 void AXS(void); // AXS AND X with accumulator, store result in X, subtract byte from X w/o borrow 00230 void DCP(void); // DCP Decrement memory without borrow 00231 void DOP(void); // DOP Double NO-OP, argument = no significance 00232 void ISC(void); // ISC INC memory, then A - M with borrow 00233 void KIL(void); //*KIL KIL a.k.a. HLT (halt) 00234 void LAX(void); 00235 void LAR(void); // LAR Load accumulator and X with memory 00236 //void NOP(void); // NOP Other NOP's 00237 void RLA(void); //*RLA Rotate memory one bit left, then AND accumulator with memory 00238 void RRA(void); //*RRA Rotate memory one bit right, then ADC 00239 //void SBC(void); // SBC extra SBC 00240 void SLO(void); //*SLO Shift memory left one bit, then ORA 00241 void SRE(void); //*SRE Shift memory right one bit, then EOR 00242 void SXA(void); // SXA AND X reg with (high byte of target addr)+1, store result in memory 00243 void SYA(void); // SYA Same as SXA, but with Y 00244 void TOP(void); // TOP Tripple NOP, arguments have no significance 00245 void XAA(void); //.XAA Unknown 00246 void XAS(void); //*XAS SP = X & A, M = SP & (high byte of target addr)+1}; 00247 //* = Unsure of correctness 00248 //. = not implemented. 00249 }; 00250 00251 #endif