├── 6502.min.zip ├── 6502.min.js.gz ├── ect-0.8.3.exe ├── demo └── nestest.nes ├── .gitattributes ├── op.txt ├── reshuffled.txt ├── 6502.min.js ├── README.md └── 6502.js /6502.min.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xem/mini6502/HEAD/6502.min.zip -------------------------------------------------------------------------------- /6502.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xem/mini6502/HEAD/6502.min.js.gz -------------------------------------------------------------------------------- /ect-0.8.3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xem/mini6502/HEAD/ect-0.8.3.exe -------------------------------------------------------------------------------- /demo/nestest.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xem/mini6502/HEAD/demo/nestest.nes -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /op.txt: -------------------------------------------------------------------------------- 1 | D=1 2 | p=r(a),C=X-p>=0,F(X-p) 3 | p=r(a),C=Y-p>=0,F(Y-p) 4 | p=r(a),C=p>>7,w(a,F(2*p)),c++ 5 | C=A>>7,A=F(2*A+(1&P)) 6 | p=r(a),C=p>>7,w(a,F(2*p+(1&P))),c++ 7 | C=1&A,A=F(A>>1) 8 | p=r(a),C=1&p,w(a,F(p>>1)),c++ 9 | X=F(X-1) 10 | p=r(a),F(p&A),N=p>>7&1,V=p>>6&1 11 | C=1&A,A=F((A>>1)+128*(1&P)) 12 | w(a,F(r(a)+1)),c++ 13 | X=F(X+1) 14 | Y=F(Y-1) 15 | Y=F(Y+1) 16 | Y=F(r(a)) 17 | p=r(a),C=1&p,w(a,F((p>>1)+128*(1&P))),c++ 18 | C=0 19 | I=1 20 | D=0 21 | I=0 22 | A=F(r(a)) 23 | A=F(r(a)&A) 24 | p=r(a),C=A-p>=0,F(A-p) 25 | p=r(a),t=A+C-1-p,V=!!(128&(A^p))&&!!(128&(A^t)),C=t>=0,A=F(t) 26 | p=r(a),t=A+C+p,V=!(128&(A^p))&&!!(128&(A^t)),C=t>255,A=F(t) 27 | o>76&&(a=r(a)+256*r(a+1-256*((a&255)==255))),PC=a-1 28 | C&&(c+=1+(a>>8!=PC+1>>8),PC=a) 29 | N&&(c+=1+(a>>8!=PC+1>>8),PC=a) 30 | N||(c+=1+(a>>8!=PC+1>>8),PC=a) 31 | V||(c+=1+(a>>8!=PC+1>>8),PC=a) 32 | X=F(r(a)) 33 | A=F(r(a)^A) 34 | w(a,F((r(a)-1)&255)),c++ 35 | C=A>>7,A=F(2*A) 36 | h(PC>>8),h(255&PC),PC=a-1,c++ 37 | C=1 38 | A=F(r(a)|A) 39 | h(A) 40 | h(P|16) 41 | A=F(g()),c++ 42 | f(g()&239),c++ 43 | f(g()),PC=g()+256*g()-1,c++ 44 | S=X 45 | Y=F(A) 46 | w(a,A) 47 | w(a,X) 48 | X=F(S) 49 | X=F(A) 50 | w(a,Y) 51 | A=F(Y) 52 | A=F(X) 53 | PC=g()+256*g(c+=2) 54 | op(3,1) 55 | C||(c+=1+(a>>8!=PC+1>>8),PC=a) 56 | V&&(c+=1+(a>>8!=PC+1>>8),PC=a) 57 | Z&&(c+=1+(a>>8!=PC+1>>8),PC=a) 58 | Z||(c+=1+(a>>8!=PC+1>>8),PC=a) 59 | V=0 -------------------------------------------------------------------------------- /reshuffled.txt: -------------------------------------------------------------------------------- 1 | S=X 2 | p=r(a),C=X-p>=0,F(X-p) 3 | p=r(a),C=Y-p>=0,F(Y-p) 4 | p=r(a),C=p>>7,w(a,F(2*p)),c++ 5 | C=A>>7,A=F(2*A+(1&P)) 6 | p=r(a),C=p>>7,w(a,F(2*p+(1&P))),c++ 7 | C=1&A,A=F(A>>1) 8 | p=r(a),C=1&p,w(a,F(p>>1)),c++ 9 | X=F(X-1) 10 | p=r(a),F(p&A),N=p>>7&1,V=p>>6&1 11 | C=1&A,A=F((A>>1)+128*(1&P)) 12 | w(a,F(r(a)+1)),c++ 13 | X=F(X+1) 14 | Y=F(Y-1) 15 | Y=F(Y+1) 16 | Y=F(r(a)) 17 | p=r(a),C=1&p,w(a,F((p>>1)+128*(1&P))),c++ 18 | C=0 19 | I=1 20 | D=0 21 | I=0 22 | A=F(r(a)) 23 | A=F(r(a)&A) 24 | p=r(a),C=A-p>=0,F(A-p) 25 | p=r(a),t=A+C-1-p,V=!!(128&(A^p))&&!!(128&(A^t)),C=t>=0,A=F(t) 26 | p=r(a),t=A+C+p,V=!(128&(A^p))&&!!(128&(A^t)),C=t>255,A=F(t) 27 | C&&(c+=1+(a>>8!=PC+1>>8),PC=a) 28 | N&&(c+=1+(a>>8!=PC+1>>8),PC=a) 29 | Z&&(c+=1+(a>>8!=PC+1>>8),PC=a) 30 | N||(c+=1+(a>>8!=PC+1>>8),PC=a) 31 | V&&(c+=1+(a>>8!=PC+1>>8),PC=a) 32 | Z||(c+=1+(a>>8!=PC+1>>8),PC=a) 33 | V||(c+=1+(a>>8!=PC+1>>8),PC=a) 34 | X=F(r(a)) 35 | A=F(r(a)^A) 36 | w(a,F((r(a)-1)&255)),c++ 37 | C=A>>7,A=F(2*A) 38 | h(PC>>8),h(255&PC),PC=a-1,c++ 39 | C=1 40 | D=1 41 | V=0 42 | A=F(r(a)|A) 43 | h(A) 44 | h(P|16) 45 | A=F(g()),c++ 46 | f(g()&239),c++ 47 | f(g()),PC=g()+256*g()-1,c++ 48 | C||(c+=1+(a>>8!=PC+1>>8),PC=a) 49 | op(3,1) 50 | Y=F(A) 51 | w(a,A) 52 | w(a,X) 53 | X=F(S) 54 | X=F(A) 55 | w(a,Y) 56 | A=F(Y) 57 | A=F(X) 58 | PC=g()+256*g(c+=2) 59 | o>76&&(a=r(a)+256*r(a+1-256*((a&255)==255))),PC=a-1 -------------------------------------------------------------------------------- /6502.min.js: -------------------------------------------------------------------------------- 1 | m=[A=X=Y=c=0] 2 | r=(d,b)=>m[c++,d%65536] 3 | w=(d,b)=>m[c++,d%65536]=b 4 | F=(d,b)=>(Z=(d&=255)<1,N=d>>7,d) 5 | f=(d,b)=>(C=d&1,Z=d>>1&1,I=d>>2&1,D=d>>3&1,B=d>>4&1,V=d>>6&1,N=d>>7) 6 | f(P=36) 7 | h=(d,b)=>(w(256+S--,d),S&=255) 8 | g=(d,b)=>r(256+(S=255&S+1)) 9 | O=[...Array(255)].map((d,b)=>Function(`c--,PC++ 10 | a=a+p-256*(p>>7),PC++ 11 | a=r(p+X&255)+256*r(p+X+1&255),PC++,c++ 12 | a=r(p)+256*r(p+1&255)+Y,c+=a-Y>>8>8||o>>4==9,PC++ 13 | a=r(a)+X&255,PC++ 14 | a=r(a)+Y&255,PC++ 15 | a=p,PC++ 16 | a=p+256*r(PC+=2) 17 | t=p+256*r(PC+=2),c+=t>>8>8||o>>4==9,a=t+Y 18 | t=p+256*r(PC+=2),c+=t>>8>8||o>>4==9||o%16>13,a=t+X`.split` 19 | `[`020666Z0Z77713Z444Z8Z999720666Z0Z77713Z444Z8Z999Z20666Z0Z77713Z444Z8Z999Z20666Z0Z77713Z444Z8Z999020666Z0Z77713Z445Z8Z998020666Z0Z77713Z445Z8Z998020666Z0Z77713Z444Z8Z999020666Z0Z77713Z444Z8Z999`[b-(b>>2)]]+` 20 | `+`S=X 21 | p=r(a),C=X-p>=0,F(X-p) 22 | p=r(a),C=Y-p>=0,F(Y-p) 23 | p=r(a),C=p>>7,w(a,F(2*p)),c++ 24 | C=A>>7,A=F(2*A+(1&P)) 25 | p=r(a),C=p>>7,w(a,F(2*p+(1&P))),c++ 26 | C=1&A,A=F(A>>1) 27 | p=r(a),C=1&p,w(a,F(p>>1)),c++ 28 | X=F(X-1) 29 | p=r(a),F(p&A),N=p>>7&1,V=p>>6&1 30 | C=1&A,A=F((A>>1)+128*(1&P)) 31 | w(a,F(r(a)+1)),c++ 32 | X=F(X+1) 33 | Y=F(Y-1) 34 | Y=F(Y+1) 35 | Y=F(r(a)) 36 | p=r(a),C=1&p,w(a,F((p>>1)+128*(1&P))),c++ 37 | C=0 38 | I=1 39 | D=0 40 | I=0 41 | A=F(r(a)) 42 | A=F(r(a)&A) 43 | p=r(a),C=A-p>=0,F(A-p) 44 | p=r(a),t=A+C-1-p,V=!!(128&(A^p))&&!!(128&(A^t)),C=t>=0,A=F(t) 45 | p=r(a),t=A+C+p,V=!(128&(A^p))&&!!(128&(A^t)),C=t>255,A=F(t) 46 | C&&(c+=1+(a>>8!=PC+1>>8),PC=a) 47 | N&&(c+=1+(a>>8!=PC+1>>8),PC=a) 48 | Z&&(c+=1+(a>>8!=PC+1>>8),PC=a) 49 | N||(c+=1+(a>>8!=PC+1>>8),PC=a) 50 | V&&(c+=1+(a>>8!=PC+1>>8),PC=a) 51 | Z||(c+=1+(a>>8!=PC+1>>8),PC=a) 52 | V||(c+=1+(a>>8!=PC+1>>8),PC=a) 53 | X=F(r(a)) 54 | A=F(r(a)^A) 55 | w(a,F((r(a)-1)&255)),c++ 56 | C=A>>7,A=F(2*A) 57 | h(PC>>8),h(255&PC),PC=a-1,c++ 58 | C=1 59 | D=1 60 | V=0 61 | A=F(r(a)|A) 62 | h(A) 63 | h(P|16) 64 | A=F(g()),c++ 65 | f(g()&239),c++ 66 | f(g()),PC=g()+256*g()-1,c++ 67 | C||(c+=1+(a>>8!=PC+1>>8),PC=a) 68 | op(3,1) 69 | Y=F(A) 70 | w(a,A) 71 | w(a,X) 72 | X=F(S) 73 | X=F(A) 74 | w(a,Y) 75 | A=F(Y) 76 | A=F(X) 77 | PC=g()+256*g(c+=2) 78 | PC=a-1 79 | PC=r(a)+256*r(a+1-256*((a&255)==255))-1`.split` 80 | `[`PI#PI#KIDPI#=I#PI#1IDPI#E6%)6%M6$)6%;6%E6%F6%E6%NB'NB'JB&ZB'@B'NB'4B'NB'Y90Y90L9*[90>90Y90290Y90VRSVRS-zXVRSORSVRSWR VRS/5A/5AQ5U/5A:5A/5AH5T/5A"7C"7C.7("7C?7C"7C37C"7C!8+!8+,8z!8+<8+!8+G8+!8+`[b-(b>>2)].charCodeAt()-32])) 81 | op=(d,b)=>(o=r(PC),p=r(a=PC+1),d?(2-d?(h(PC>>8),h(255&PC),h(b|239&P)):(S=S-3&255,c=6),I=1,PC=r(65528+2*d)+256*r(65528+2*d+1)):(O[o](),PC++,PC&=65535),P=C+2*Z+4*I+8*D+16*B+32+64*V+128*N) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mini6502 2 | ======== 3 | 4 | An attempt to make the smallest standalone NES CPU (Ricoh 6502) simulator in JavaScript (< 1kb zipped) 5 | 6 | Developed for my mini NES emulator: https://github.com/xem/jsnes-lite 7 | 8 | A writeup is available on: https://xem.github.io/articles/nes.html 9 | 10 | Commented source code: https://github.com/xem/mini6502/blob/gh-pages/6502.js 11 | 12 | Minified: https://github.com/xem/mini6502/blob/gh-pages/6502.min.js 13 | 14 | Zipped: https://github.com/xem/mini6502/blob/gh-pages/6502.min.js.zip (1015b) 15 | 16 | Gzipped: https://github.com/xem/mini6502/blob/gh-pages/6502.min.js.gz (928b) 17 | 18 | Demo/test: https://xem.github.io/mini6502/demo 19 | 20 |
21 |
22 | 23 | Features: 24 | 25 | - Initialization of memory (m), registers (A, X, Y, S, PC, P), flags (C, Z, I, D, B, V, N) and cycle counter (c) as global vars 26 | - Memory helpers (read: `r(addr)`, write: `w(addr,value)`, push: `h(value)`, pop: `g()`) 27 | - Emulation of the 151 official 6502 opcodes and 3 interrupts 28 | - Emulation of the hardware bugs and quirks (JMP, cross-page reads...) 29 | - Cycle-accurate 30 | - Optimized to run properly at 1.76M cycles/second 31 | 32 |
33 |
34 | 35 | Features not included: 36 | 37 | - NES specific memory mirrorings (showing the same data at different addresses) 38 | - NES specific I/O registers (to handle graphics, sound, joypads...) 39 | - NES Mappers (cartridge models, allowing ROM bank switching, save slots, ...) 40 | - Decimal mode (which is present on the original MOS 6502 CPU but not in Ricoh 6502 used by the NES) 41 | 42 |
43 |
44 | 45 | How to use: 46 | 47 | - Include 6502.js or 6502.min.js in your project 48 | - Put the contents of a ROM in the memory m (an array of integers between 0 and 255) 49 | - Reset the CPU with `op(2)` or set PC at the address where the program starts 50 | - Call `op()` repeatedly to simulate opcodes one by one 51 | - Call `op(1)` for a NMI interrupt, `op(2)` for a Reset and `op(3)` for an IRQ 52 | 53 |
54 |
55 | 56 | How to compress after golfing: 57 | 58 | - zip: add 6502.min.js in a zip file, run `./ect-0.8.3.exe -9 -zip .\6502.min.zip` 59 | - gzip: run `zopfli --gzip -i1000000 6502.min.js` 60 | - reshuffle opcode list: `out.txt` 61 | 62 |
63 |
64 | 65 | Useful resources: 66 | - https://wiki.nesdev.com/w/index.php/CPU_unofficial_opcodes 67 | - https://wiki.nesdev.com/w/index.php/Status_flags 68 | - http://wiki.nesdev.com/w/index.php/CPU_interrupts 69 | - https://wiki.nesdev.com/w/index.php/Stack 70 | - https://www.masswerk.at/6502/6502_instruction_set.html 71 | - https://problemkaputt.de/everynes.htm#cpu65xxmicroprocessor 72 | - https://www.npmjs.com/package/dict-tempering 73 | - http://www.6502.org/tutorials/vflag.html 74 | - https://retrocomputing.stackexchange.com/questions/145 75 | - http://forum.6502.org/viewtopic.php?f=8&t=6370 76 | - https://twitter.com/subzey/status/1326227026076971008 77 | - https://twitter.com/subzey/status/1328659977150623750 78 | - https://twitter.com/subzey/status/1328449127471083520 79 | -------------------------------------------------------------------------------- /6502.js: -------------------------------------------------------------------------------- 1 | // mini 6502 CPU simulator 2 | // ======================= 3 | 4 | // Globals 5 | // ------- 6 | 7 | // 16kb memory 8 | // Each chunk of 256 bytes in memory is called a page 9 | // The first chunk ($00-$FF) is called Zero page and is easier/faster to access 10 | m = [ 11 | 12 | // Registers 13 | A = // accumulator 14 | X = // X 15 | Y = // Y 16 | // S = // stack pointer (also called SP) 17 | // PC = // program counter (address of next instruction) 18 | // P = // status register (flags on bytes 0-7: C=0, Z=0, I=1, D=0, B=0, 1, V=0, N=0) 19 | 20 | // Other globals 21 | 22 | // t, // temp var 23 | // o, // opcode value 24 | // a, // operand address 25 | // p, // operand value 26 | c = 0 // cycle counter 27 | 28 | ], 29 | 30 | 31 | // Helpers 32 | // ------- 33 | 34 | // Read a byte from memory. (costs 1 cycle) 35 | // The address is wrapped between $0000 and $FFFF 36 | r = v => m[c++, v % 0x10000], 37 | 38 | // Write a byte in memory. (costs 1 cycle) 39 | w = (v, w) => m[c++, v % 0x10000] = w, 40 | 41 | // Update N and Z status flags: 42 | // - The value v is clamped on 8 bits and returned 43 | // - The Zero flag (bit 1 of P) is set if v is zero, otherwise it's cleared 44 | // - The Negative flag (bit 7 of P) is set if byte 7 of v is 1, otherwise it's cleared 45 | F = v => ( 46 | Z = (v &= 255) < 1, 47 | N = v >> 7, 48 | v 49 | ), 50 | 51 | // Update the flags values according to the status register P 52 | f = v => ( 53 | C = v & 1, 54 | Z = (v>>1) & 1, 55 | I = (v>>2) & 1, 56 | D = (v>>3) & 1, 57 | B = (v>>4) & 1, 58 | V = (v>>6) & 1, 59 | N = v>>7 60 | ), 61 | 62 | // Set all flags on load 63 | f(P = 0x24), 64 | 65 | // Push on Stack 66 | // Write at address $100 + S, decrement S, wrap it between $00 and $FF 67 | h = v => ( 68 | w(256 + S--, v), 69 | S &= 255 70 | ), 71 | 72 | // Pull from stack 73 | // Increment S, wrap it between $00 and $FF, read at address $100 + S 74 | g = v => r(256 + (S = (255 & (S+1)))), 75 | 76 | // Instructions 77 | // ============ 78 | 79 | // The code below creates a function for each valid opcode supported by the CPU. 80 | // When a function is called: 81 | // - PC represents the current opcode's address 82 | // - o is the opcode's value 83 | // - a equals PC+1 84 | // - p is the value stored at the address PC+1 85 | // - c (the cycle counter) equals 2 because two memory reads have already been done (o and p) 86 | O = [...Array(255)].map((t,o) => 87 | Function( 88 | 89 | ( 90 | 91 | // Addressing modes 92 | // ---------------- 93 | 94 | // Some opcodes require an address in memory 95 | // This address can be computed in 11 different ways 96 | // The 10 main ones are implemented here, the 11th is included in the last instruction (JMP ind) 97 | // The order and implementations below are optimized for a better gzip compression 98 | 99 | // "0": Immediate: 100 | // The target address is PC+1, already stored in a 101 | // Opcode size: 2 bytes 102 | // Cycles total: 2 103 | // Cycles addr.: -1 (1 cycle is removed because the first p fetch is redundant, the instruction has to read it again) 104 | // Cycles opc. : 1 105 | "c--,PC++;" 106 | 107 | // "1": Relative: 108 | // (only used for branching) 109 | // The target address (between PC-128 and PC+127) = PC + signed offset stored in p 110 | // Opcode size: 2 bytes 111 | // Cycles total: 2 (no branch) / 3 (branch on same page) / 4 (branch on another page) 112 | // Cycles addr.: 0 113 | // Cycles opc. : 0-2 114 | + "a=a+p-256*(p>>7),PC++;" 115 | 116 | // "2": Indexed indirect X 117 | // The target address is absolute and stored at a zero page address which is stored at PC + 1 + X 118 | // Opcode size: 2 bytes 119 | // Cycles total: 6 (read or write) 120 | // Cycles addr.: 3 121 | // Cycles opc. : 1 122 | + "a=r(p+X&255)+256*r(p+X+1&255),PC++,c++;" 123 | 124 | // "3": Indirect indexed Y 125 | // The target address is absolute and stored at a zero page address which is stored at PC+1, then Y is added to it 126 | // Opcode size: 2 bytes 127 | // Cycles total: 5* (read) / 6 (write) 128 | // Cycles addr.: 2-3 129 | // Cycles opc. : 0-1 130 | // * Cross-page read (if address and address + Y are on different pages) costs 1 extra cycle 131 | + "a=r(p)+256*r(p+1&255)+Y,c+=a-Y>>8
>8||o>>4==9,PC++;" 132 | 133 | // "4": Zero page X 134 | // The target address is equal to zero page address (stored at PC+1) + X, wrapping between $00 and $FF 135 | // Opcode size: 2 bytes 136 | // Cycles total: 3 (BIT) / 4 (read or write) / 6 (read + write) 137 | // Cycles addr.: 1 138 | // Cycles opc. : 0-2 139 | + "a=r(a)+X&255,PC++;" 140 | 141 | // "5": Zero page Y 142 | // The target address is equal to zero page address (stored at PC+1) + Y, wrapping between $00 and $FF 143 | // Opcode size: 2 bytes 144 | // Cycles total: 4 (read or write) 145 | // Cycles addr.: 1 146 | // Cycles opc. : 1 147 | + "a=r(a)+Y&255,PC++;" 148 | 149 | // "6": Zero page 150 | // The target address (between $00 and $FF) is stored in p 151 | // Opcode size: 2 bytes 152 | // Cycles total: 3 (read or write) / 5 (read + write) 153 | // Cycles addr.: 0 154 | // Cycles opc. : 1-3 155 | + "a=p,PC++;" 156 | 157 | // "7": Absolute 158 | // The target address is stored at PC+1 (low byte) and PC+2 (high byte) 159 | // Opcode size: 3 bytes 160 | // Cycles total: 3 (JMP) / 4 (read or write) / 6 (read + write or JSR) 161 | // Cycles addr.: 1 162 | // Cycles opc. : 0-3 163 | + "a=p+256*r(PC+=2);" 164 | 165 | // "8": Absolute Y 166 | // The target address is equal to absolute address (stored at PC+1 and PC+2) + Y 167 | // Opcode size: 3 bytes 168 | // Cycles total: 4* (read) / 5 (write) 169 | // Cycles addr.: 1-2 170 | // Cycles opc. : 0-2 171 | // * Cross-page read (if address and address + Y are on different pages) costs 1 extra cycle 172 | + "t=p+256*r(PC+=2),c+=t>>8>8||o>>4==9,a=t+Y;" 173 | 174 | // "9": Absolute X 175 | // The target address is equal to absolute address (stored at PC+1 and PC+2) + X 176 | // Opcode size: 3 bytes 177 | // Cycles total: 4* (read) / 5 (write) / 7 (read + write) 178 | // Cycles addr.: 1-2 179 | // Cycles opc. : 0-4 180 | // * Cross-page read (if address and address + X are on different pages) costs 1 extra cycle 181 | + "t=p+256*r(PC+=2),c+=t>>8>8||o>>4==9||(15&o)>13,a=t+X" 182 | 183 | // "Z": implicit or Accumulator 184 | // The target is either a flag or a CPU register (no need to compute an address) 185 | // (When a "Z" is read, the generated JavaScript code will just contain "undefined;") 186 | // Opcode size: 1 byte (no need to increment PC) 187 | // Cycles total: 2-7 188 | // Cycles addr.: 0 189 | // Cycles opc. : 0-5 190 | + "" 191 | 192 | // Make an array from this string 193 | ).split(";") 194 | 195 | // Fetch the right addressing mode for the current opcode (ignore illegal opcode where o % 4 == 3): 196 | // (The string below is optomized for compression: the illegal opcodes are assigned characters that create extra repetitions) 197 | [ 198 | ( 199 | "020666Z0Z77713Z444Z8Z999" 200 | +"720666Z0Z77713Z444Z8Z999" 201 | +"Z20666Z0Z77713Z444Z8Z999" 202 | +"Z20666Z0Z77713Z444Z8Z999" 203 | +"020666Z0Z77713Z445Z8Z998" 204 | +"020666Z0Z77713Z445Z8Z998" 205 | +"020666Z0Z77713Z444Z8Z999" 206 | +"020666Z0Z77713Z444Z8Z999" 207 | )[o-(o>>2)] 208 | ] 209 | 210 | // Separator 211 | + ";" 212 | 213 | // Instructions 214 | // ------------ 215 | 216 | // There are 56 official instructions, performing operations in memory and/or in the registers 217 | // Some instructions use extra cycles: 218 | // * : cross-page when fetching the address costs 1 extra cycle 219 | // ** : Same-page branch (PC+2>>8 == a>>8) costs 1 extra cycle. Cross-page branch costs 2 extra cycles 220 | // ***: Instructions that read, modify and write a value in memory (+ JSR/RTI/RTS/PLA/PLP) cost 1 to 2 extra cycles 221 | // The order and implementations below are also optimized for a better gzip compression 222 | // Also, some instructions were splitted in two if they target either the memory or the Accumulator register (ROR, ROL, LSR, ASL) 223 | 224 | + ( 225 | 226 | // " ": TXS (transfer X to stack pointer) 227 | // Stack pointer = X 228 | // Addressing: imp 229 | // Opcode: 9A 230 | // Cycles total: 2 231 | // Cycles addr.: 0 232 | // Cycles opc. : 0 233 | "S=X;" 234 | 235 | // "!": CPX (compare memory and X) 236 | // N, Z and C are set with the result of X minus a byte in memory 237 | // Flag C is set if there's no borrow 238 | // Addressings: imm, zpg, abs 239 | // Opcodes: E0, E4, EC 240 | // Cycles total: 2, 3, 4 241 | // Cycles addr.: -1, 0, 1 242 | // Cycles opc. : 1, 1, 1 243 | + "p=r(a),C=X-p>=0,F(X-p);" 244 | 245 | // '"': CPY (compare memory and Y) 246 | // N, Z and C are set with the result of Y minus a byte in memory 247 | // Flag C is set if there's no borrow 248 | // Addressings: imm, zpg, abs 249 | // Opcodes: C0, C4, CC 250 | // Cycles total: 2, 3, 4 251 | // Cycles addr.: -1, 0, 1 252 | // Cycles opc. : 1, 1, 1 253 | + "p=r(a),C=Y-p>=0,F(Y-p);" 254 | 255 | // "#": ASL (shift left) 256 | // A byte in memory is left shifted. Flags: N, Z, C 257 | // The shifted-out bit 7 is saved in C 258 | // Addressings: zpg, zpgX, abs, absX 259 | // Opcodes: 06, 16, 0E, 1E 260 | // Cycles total: 5, 6, 6, 7 261 | // Cycles addr.: 0, 1, 1, 2 262 | // Cycles opc. : 3, 3, 3, 3 (***) 263 | + "p=r(a),C=p>>7,w(a,F(2*p)),c++;" 264 | 265 | // "$": ROL A (rotate left accumulator) 266 | // Rotate left A. Same as left shift but C flag is put into bit 0. Flags: N, Z, C 267 | // The shifted-out bit 7 is saved in C 268 | // Addressing: A 269 | // Opcode: 2A 270 | // Cycles total: 2 271 | // Cycles addr.: 0 272 | // Cycles opc. : 0 273 | + "C=A>>7,A=F(2*A+(1&P));" 274 | 275 | // "%": ROL (rotate left) 276 | // Rotate left a byte in memory. Same as left shift but C flag is put into bit 0. Flags: N, Z, C 277 | // The shifted-out bit 7 is saved in C 278 | // Addressings: zpg, zpgX, abs, absX 279 | // Opcodes: 26, 36, 2E, 3E 280 | // Cycles total: 5, 6, 6, 7 281 | // Cycles addr.: 0, 1, 1, 2 282 | // Cycles opc. : 3, 3, 3, 3 (***) 283 | + "p=r(a),C=p>>7,w(a,F(2*p+(1&P))),c++;" 284 | 285 | // "&": LSR A (shift right accumulator) 286 | // A is shifted right. Flags: N, Z, C 287 | // The shifted-out bit 0 is saved in C 288 | // Addressing: A 289 | // Opcode: 4A 290 | // Cycles total: 2 291 | // Cycles addr.: 0 292 | // Cycles opc. : 0 293 | + "C=1&A,A=F(A>>1);" 294 | 295 | // "'": LSR (shift right) 296 | // A or a byte in memory is shifted right. Flags: N, Z, C 297 | // The shifted-out bit 0 is saved in C 298 | // Addressings: zpg, zpgX, abs, absX 299 | // Opcodes: 46, 56, 4E, 5E 300 | // Cycles total: 5, 6, 6, 7 301 | // Cycles addr.: 0, 1, 1, 2 302 | // Cycles opc. : 3, 3, 3, 3 (***) 303 | + "p=r(a),C=1&p,w(a,F(p>>1)),c++;" 304 | 305 | // "(": DEX (decrement X) 306 | // X is decremented. Flags: N, Z 307 | // Addressing: imp 308 | // Opcode: CA 309 | // Cycles total: 2 310 | // Cycles addr.: 0 311 | // Cycles opc. : 0 312 | + "X=F(X-1);" 313 | 314 | // ")": BIT (test bits in memory) 315 | // N and V = bits 7 and 6 of operand. Z is set if operand AND A is not zero. Flags: N, Z, V 316 | // Addressings: zpg, abs 317 | // Opcodes: 24, 2C 318 | // Cycles total: 3, 4 319 | // Cycles addr.: 0, 1 320 | // Cycles opc. : 1, 1 321 | + "p=r(a),F(p&A),N=p>>7&1,V=p>>6&1;" 322 | 323 | // "*": ROR A (rotate right accumulator) 324 | // Rotate right A or a byte in memory. Same as left shift but C flag is put into bit 7. Flags: N, Z, C 325 | // The shifted-out bit 0 is saved in C 326 | // Addressing: A 327 | // Opcode: 6A 328 | // Cycles total: 2 329 | // Cycles addr.: 0 330 | // Cycles opc. : 0 331 | + "C=1&A,A=F((A>>1)+128*(1&P));" 332 | 333 | // "+": INC (increment memory) 334 | // A byte in memory is incremented. Flags: N, Z 335 | // Addressings: zpg, zpgX, abs, absX 336 | // Opcodes: E6, F6, EE, FE 337 | // Cycles total: 5, 6, 6, 7 338 | // Cycles addr.: 0, 1, 1, 2 339 | // Cycles opc. : 3, 3, 3, 3 (***) 340 | + "w(a,F(r(a)+1)),c++;" 341 | 342 | // ",": INX (increment X) 343 | // X is incremented. Flags: N, Z 344 | // Addressing: imp 345 | // Opcode: E8 346 | // Cycles total: 2 347 | // Cycles addr.: 0 348 | // Cycles opc. : 0 349 | + "X=F(X+1);" 350 | 351 | // "-": DEY (decrement Y) 352 | // Y is decremented. Flags: N, Z 353 | // Addressing: imp 354 | // Opcode: 88 355 | // Cycles total: 2 356 | // Cycles addr.: 0 357 | // Cycles opc. : 0 358 | + "Y=F(Y-1);" 359 | 360 | // ".": INY (increment Y) 361 | // Y is incremented. Flags: N, Z 362 | // Addressing: imp 363 | // Opcode: C8 364 | // Cycles total: 2 365 | // Cycles addr.: 0 366 | // Cycles opc. : 0 367 | + "Y=F(Y+1);" 368 | 369 | // "/": LDY (load Y with memory) 370 | // Y = a byte from memory. Flags: N, Z 371 | // Addressings: imm, zpg, zpgX, abs, absX 372 | // Opcodes: A0, A4, B4, AC, BC 373 | // Cycles total: 2, 3, 4, 4, 4* 374 | // Cycles addr.: -1, 0, 1, 1, 1* 375 | // Cycles opc. : 1, 1, 1, 1, 1 376 | + "Y=F(r(a));" 377 | 378 | // 0: ROR (rotate right) 379 | // Rotate right a byte in memory. Same as left shift but C flag is put into bit 7. Flags: N, Z, C 380 | // The shifted-out bit 0 is saved in C 381 | // Addressings: zpg, zpgX, abs, absX 382 | // Opcodes: 66, 76, 6E, 7E 383 | // Cycles total: 5, 6, 6, 7 384 | // Cycles addr.: 0, 1, 1, 2 385 | // Cycles opc. : 3, 3, 3, 3 (***) 386 | + "p=r(a),C=1&p,w(a,F((p>>1)+128*(1&P))),c++;" 387 | 388 | // "1": CLC (clear carry flag) 389 | // C is set to 0 390 | // Addressing: imp 391 | // Opcode: 18 392 | // Cycles total: 2 393 | // Cycles addr.: 0 394 | // Cycles opc. : 0 395 | + "C=0;" 396 | 397 | // "2": SEI (set interrupt disable flag) 398 | // I is set to 1 399 | // Addressing: imp 400 | // Opcode: 78 401 | // Cycles total: 2 402 | // Cycles addr.: 0 403 | // Cycles opc. : 0 404 | + "I=1;" 405 | 406 | // "3": CLD (clear decimal flag) 407 | // D is set to 0 408 | // Addressing: imp 409 | // Opcode: D8 410 | // Cycles total: 2 411 | // Cycles addr.: 0 412 | // Cycles opc. : 0 413 | + "D=0;" 414 | 415 | // "4": CLI (clear interrupt disable flag) 416 | // I is set to 0 417 | // Addressing: imp 418 | // Opcode: 58 419 | // Cycles total: 2 420 | // Cycles addr.: 0 421 | // Cycles opc. : 0 422 | + "I=0;" 423 | 424 | // "5": LDA (load accumulator with memory) 425 | // A = a byte from memory. Flags: N, Z 426 | // Addressings: imm, zpg, zpgX, abs, absX, absY, indX, indY 427 | // Opcodes: A9, A5, B5, AD, BD, B9, A1, B1 428 | // Cycles total: 2, 3, 4, 4, 4*, 4*, 6, 5* 429 | // Cycles addr.: -1, 0, 1, 1, 1*, 1* 3, 3* 430 | // Cycles opc. : 1, 1, 1, 1, 1, 1, 1, 1 431 | + "A=F(r(a));" 432 | 433 | // "6": AND: (AND memory and accumulator) 434 | // A = A AND a byte in memory. Flags: N, Z 435 | // Addressings: imm, zpg, zpgX, abs, absX, absY, indX, indY 436 | // Opcodes: 29, 25, 35, 2D, 3D, 39, 21, 31 437 | // Cycles total: 2, 3, 4, 4, 4*, 4*, 6, 5* 438 | // Cycles addr.: -1, 0, 1, 1, 1*, 1* 3, 3* 439 | // Cycles opc. : 1, 1, 1, 1, 1, 1, 1, 1 440 | + "A=F(r(a)&A);" 441 | 442 | // "7": CMP (compare memory and accumulator) 443 | // N, Z and C are set with the result of A - a byte in memory 444 | // Flag C is set if there's no borrow 445 | // Addressings: imm, zpg, zpgX, abs, absX, absY, indX, indY 446 | // Opcodes: C9, C5, D5, CD, DD, D9, C1, D1 447 | // Cycles total: 2, 3, 4, 4, 4*, 4*, 6, 5* 448 | // Cycles addr.: -1, 0, 1, 1, 1*, 1* 3, 3* 449 | // Cycles opc. : 1, 1, 1, 1, 1, 1, 1, 1 450 | + "p=r(a),C=A-p>=0,F(A-p);" 451 | 452 | // "8": SBC (subtract from accumulator with carry) 453 | // A = A - a byte from memory - (1 - Carry). Flags: N, Z, C, V 454 | // Flag C is set if there's no borrow 455 | // Flag V is set if the subtraction is incorrectly considered positive 456 | // Addressings: imm, zpg, zpgX, abs, absX, absY, indX, indY 457 | // Opcodes: E9, E5, F5, ED, FD, F9, E1, F1 458 | // Cycles total: 2, 3, 4, 4, 4*, 4*, 6, 5* 459 | // Cycles addr.: -1, 0, 1, 1, 1*, 1* 3, 3* 460 | // Cycles opc. : 1, 1, 1, 1, 1, 1, 1, 1 461 | + "p=r(a),t=A+C-1-p,V=!!(128&(A^p))&&!!(128&(A^t)),C=t>=0,A=F(t);" 462 | 463 | // "9": ADC (add to accumulator with carry) 464 | // A = A + a byte in memory + Carry. Flags: N, Z, C, V 465 | // Flag C is set if there's a carry 466 | // Flag V is set if the sum of two positive numbers is incorrectly considered negative 467 | // Addressings: imm, zpg, zpgX, abs, absX, absY, indX, indY 468 | // Opcodes: 69, 65, 75, 6D, 7D, 79, 61, 71 469 | // Cycles total: 2, 3, 4, 4, 4*, 4*, 6, 5* 470 | // Cycles addr.: -1, 0, 1, 1, 1*, 1* 3, 3* 471 | // Cycles opc. : 1, 1, 1, 1, 1, 1, 1, 1 472 | + "p=r(a),t=A+C+p,V=!(128&(A^p))&&!!(128&(A^t)),C=t>255,A=F(t);" 473 | 474 | // ":": BCS (branch on carry set) 475 | // PC = address if C is 1 476 | // Addressing: rel 477 | // Opcode: B0 478 | // Cycles total: 2** 479 | // Cycles addr.: 0 480 | // Cycles opc. : 0** 481 | + "C&&(c+=1+(a>>8!=PC+1>>8),PC=a);" 482 | 483 | // ";": BMI (branch on minus) 484 | // PC = address if N is 1 485 | // Addressing: rel 486 | // Opcode: 30 487 | // Cycles total: 2** 488 | // Cycles addr.: 0 489 | // Cycles opc. : 0** 490 | + "N&&(c+=1+(a>>8!=PC+1>>8),PC=a);" 491 | 492 | // "<": BEQ (branch if equal) 493 | // PC = address if Z is 0 494 | // Addressing: rel 495 | // Opcode: F0 496 | // Cycles total: 2** 497 | // Cycles addr.: 0 498 | // Cycles opc. : 0** 499 | + "Z&&(c+=1+(a>>8!=PC+1>>8),PC=a);" 500 | 501 | // "=": BPL (branch on plus) 502 | // PC = address if N is 0 503 | // Addressing: rel 504 | // Opcode: 10 505 | // Cycles total: 2** 506 | // Cycles addr.: 0 507 | // Cycles opc. : 0** 508 | + "N||(c+=1+(a>>8!=PC+1>>8),PC=a);" 509 | 510 | // ">": BVS (branch on overflow set) 511 | // PC = address if V is 1 512 | // Addressing: rel 513 | // Opcode: 70 514 | // Cycles total: 2** 515 | // Cycles addr.: 0 516 | // Cycles opc. : 0** 517 | + "V&&(c+=1+(a>>8!=PC+1>>8),PC=a);" 518 | 519 | // "?": BNE (branch if not equal) 520 | // PC = address if Z is 1 521 | // Addressing: rel 522 | // Opcode: D0 523 | // Cycles total: 2** 524 | // Cycles addr.: 0 525 | // Cycles opc. : 0** 526 | + "Z||(c+=1+(a>>8!=PC+1>>8),PC=a);" 527 | 528 | // "@": BVC (branch on overflow clear) 529 | // PC = address if V is 0 530 | // Addressing: rel 531 | // Opcode: 50 532 | // Cycles total: 2** 533 | // Cycles addr.: 0 534 | // Cycles opc. : 0** 535 | + "V||(c+=1+(a>>8!=PC+1>>8),PC=a);" 536 | 537 | // "A": LDX (load X with memory) 538 | // X = a byte from memory. Flags: N, Z 539 | // Addressings: imm, zpg, zpgY, abs, absY 540 | // Opcodes: A2, A6, B6, AE, BE 541 | // Cycles total: 2, 3, 4, 4, 4* 542 | // Cycles addr.: -1, 0, 1, 1, 1* 543 | // Cycles opc. : 1, 1, 1, 1, 1 544 | + "X=F(r(a));" 545 | 546 | // "B": EOR (exclusive-or memory and accumulator) 547 | // A = A XOR a byte in memory. Flags: N, Z 548 | // Addressings: imm, zpg, zpgX, abs, absX, absY, indX, indY 549 | // Opcodes: 49, 45, 55, 4D, 5D, 59, 41, 51 550 | // Cycles total: 2, 3, 4, 4, 4*, 4*, 6, 5* 551 | // Cycles addr.: -1, 0, 1, 1, 1*, 1* 3, 3* 552 | // Cycles opc. : 1, 1, 1, 1, 1, 1, 1, 1 553 | + "A=F(r(a)^A);" 554 | 555 | // "C": DEC (decrement memory) 556 | // A byte in memory is decremented. Flags: N, Z 557 | // Addressings: zpg, zpgX, abs, absX 558 | // Opcodes: C6, D6, CE, DE 559 | // Cycles total: 5, 6, 6, 7 560 | // Cycles addr.: 0, 1, 1, 2 561 | // Cycles opc. : 3, 3, 3, 3 (***) 562 | + "w(a,F((r(a)-1)&255)),c++;" 563 | 564 | // "D": ASL A (shift left accumulator) 565 | // A is left shifted. Flags: N, Z, C 566 | // The shifted-out bit 7 is saved in C 567 | // Addressing: A 568 | // Opcode: 0A 569 | // Cycles total: 2 570 | // Cycles addr.: 0 571 | // Cycles opc. : 0 572 | + "C=A>>7,A=F(2*A);" 573 | 574 | // "E": JSR (jump to subroutine) 575 | // Push PC + 2, PC = absolute address 576 | // Addressing: abs 577 | // Opcode: 20 578 | // Cycles total: 6 579 | // Cycles addr.: 1 580 | // Cycles opc. : 3 (***) 581 | + "h(PC>>8),h(255&PC),PC=a-1,c++;" 582 | 583 | // "F": SEC (set carry flag) 584 | // C is set to 1 585 | // Addressing: imp 586 | // Opcode: 38 587 | // Cycles total: 2 588 | // Cycles addr.: 0 589 | // Cycles opc. : 0 590 | + "C=1;" 591 | 592 | // "G": SED (set decomal flag) 593 | // D is set to 1 594 | // Addressing: imp 595 | // Opcode: F8 596 | // Cycles total: 2 597 | // Cycles addr.: 0 598 | // Cycles opc. : 0 599 | + "D=1;" 600 | 601 | // "H": CLV (clear overflow flag) 602 | // V is set to 0 603 | // Addressing: imp 604 | // Opcode: B8 605 | // Cycles total: 2 606 | // Cycles addr.: 0 607 | // Cycles opc. : 0 608 | + "V=0;" 609 | 610 | // "I": ORA (OR memory and accumulator) 611 | // A = A OR a byte in memory. Flags: N, Z. 612 | // Addressings: imm, zpg, zpgX, abs, absX, absY, indX, indY 613 | // Opcodes: 09, 05, 15, 0D, 1D, 19, 01, 11 614 | // Cycles total: 2, 3, 4, 4, 4*, 4*, 6, 5* 615 | // Cycles addr.: -1, 0, 1, 1, 1*, 1* 3, 3* 616 | // Cycles opc. : 1, 1, 1, 1, 1, 1, 1, 1 617 | + "A=F(r(a)|A);" 618 | 619 | // "J": PHA (push accumulator) 620 | // Push A 621 | // Addressing: imp 622 | // Opcode: 48 623 | // Cycles total: 3 624 | // Cycles addr.: 0 625 | // Cycles opc. : 1 626 | + "h(A);" 627 | 628 | // "K": PHP (push processor status) 629 | // Push P with B flag set to 1 630 | // Addressing: imp 631 | // Opcode: 08 632 | // Cycles total: 3 633 | // Cycles addr.: 0 634 | // Cycles opc. : 1 635 | + "h(P|16);" 636 | 637 | // A=F(g()),c++ 638 | // "L": PLA (pull accumulator) 639 | // Pull A. Flags: N, Z. 640 | // Addressing: imp 641 | // Opcode: 68 642 | // Cycles total: 4 (*** 1 extra cycle according to nestest) 643 | // Cycles addr.: 0 644 | // Cycles opc. : 1 645 | + "A=F(g()),c++;" 646 | 647 | // "M": PLP (pull processor status) 648 | // Pull P and set all flags 649 | // (According to nestest, the B flag stays at 0) 650 | // Addressing: imp 651 | // Opcode: 28 652 | // Cycles total: 4 (*** 1 extra cycle according to nestest) 653 | // Cycles addr.: 0 654 | // Cycles opc. : 1 655 | + "f(g()&239),c++;" 656 | 657 | // "N": RTI (return from interrupt) 658 | // Pull P, set all flags, pull PC 659 | // Addressing: imp 660 | // Opcode: 40 661 | // Cycles total: 6 662 | // Cycles addr.: 0 663 | // Cycles opc. : 4 (***) 664 | + "f(g()),PC=g()+256*g()-1,c++;" 665 | 666 | // "O": BCC (branch on carry clear) 667 | // PC = address if C is 0 668 | // Addressing: rel 669 | // Opcode: 90 670 | // Cycles total: 2** 671 | // Cycles addr.: 0 672 | // Cycles opc. : 0** 673 | + "C||(c+=1+(a>>8!=PC+1>>8),PC=a);" 674 | 675 | // "P": BRK (force break) 676 | // Interrupt, push PC+2 (PC+1 is a padding byte), push P with B flag set to 1, set I to 1 677 | // This is equivalent to an IRQ interrupt with another value of P pushed on the stack: 678 | // "h(PC>>8),h(255&PC),h(P|16),I=1,PC=r(65534)+256*r(65535)-1;" 679 | // Addressing: imp 680 | // Opcode: 00 681 | // Cycles total: 7 682 | // Cycles addr.: 0 683 | // Cycles opc. : 5 684 | //+ 685 | + "op(3,1);" 686 | 687 | // "Q": TAY (transfer accumulator to Y) 688 | // Y = A. Flags: N, Z 689 | // Addressing: imp 690 | // Opcode: A8 691 | // Cycles total: 2 692 | // Cycles addr.: 0 693 | // Cycles opc. : 0 694 | + "Y=F(A);" 695 | 696 | // "R": STA (store accumulator) 697 | // A is copied in memory 698 | // Addressings: zpg, zpgX, abs, absX, absY, indX, indY 699 | // Opcodes: 85, 95, 8D, 9D, 99, 81, 91 700 | // Cycles total: 3, 4, 4, 5, 5, 6, 6 701 | // Cycles addr.: 0, 1, 1, 2, 2 3, 2 702 | // Cycles opc. : 1, 1, 1, 1, 1, 1, 1 703 | + "w(a,A);" 704 | 705 | // "S": STX (store X) 706 | // X is copied in memory 707 | // Addressings: zpg, zpgY, abs 708 | // Opcodes: 86, 96, 8E 709 | // Cycles total: 3, 4, 4 710 | // Cycles addr.: 0, 1, 1 711 | // Cycles opc. : 1, 1, 1 712 | + "w(a,X);" 713 | 714 | // "T": TSX (transfer stack pointer to X) 715 | // X = S. Flags: N, Z 716 | // Addressing: imp 717 | // Opcode: BA 718 | // Cycles total: 2 719 | // Cycles addr.: 0 720 | // Cycles opc. : 0 721 | + "X=F(S);" 722 | 723 | // "U": TAX (transfer accumulator to X) 724 | // X = A. Flags: N, Z 725 | // Addressing: imp 726 | // Opcode: AA 727 | // Cycles total: 2 728 | // Cycles addr.: 0 729 | // Cycles opc. : 0 730 | + "X=F(A);" 731 | 732 | // "V": STY (store Y) 733 | // Y is copied in memory 734 | // Addressings: zpg, zpgX, abs 735 | // Opcodes: 84, 94, 8C 736 | // Cycles total: 3, 4, 4 737 | // Cycles addr.: 0, 1, 1 738 | // Cycles opc. : 1, 1, 1 739 | + "w(a,Y);" 740 | 741 | // "W": TYA (transfer Y to accumulator) 742 | // A = Y. Flags: N, Z 743 | // Addressing: imp 744 | // Opcode: 98 745 | // Cycles total: 2 746 | // Cycles addr.: 0 747 | // Cycles opc. : 0 748 | + "A=F(Y);" 749 | 750 | // "X": TXA (transfer X to accumulator) 751 | // A = X. Flags: N, Z 752 | // Addressing: imp 753 | // Opcode: 8A 754 | // Cycles total: 2 755 | // Cycles addr.: 0 756 | // Cycles opc. : 0 757 | + "A=F(X);" 758 | 759 | // "Y": RTS (return from subroutine) 760 | // Pull and increment PC 761 | // Addressing: imp 762 | // Opcode: 60 763 | // Cycles total: 6 764 | // Cycles addr.: 0 765 | // Cycles opc. : 0 (***) 766 | + "PC=g()+256*g(c+=2);" 767 | 768 | // "Z": JMP (jump to new location) 769 | // Set a new value to PC 770 | // Addressings: abs 771 | // Opcodes: 4C 772 | // Cycles total: 3 773 | // Cycles addr.: 1 774 | // Cycles opc. : 0 775 | + "PC=a-1;" 776 | 777 | // "[" JMP indirect 778 | // Jump to an address stored anywhere in memory. The address of this address is stored after the opcode 779 | // Hardware bug: if the indirect address falls on a page boundary ($xxFF), it will wrap and fetch the low byte in the same page ($xx00) 780 | // Addressing: ind 781 | // Opcodes: 6C 782 | // Cycles total: 5 783 | // Cycles addr.: 3 784 | // Cycles opc. : 2 785 | + "PC=r(a)+256*r(a+1-256*((a&255)==255))-1" 786 | 787 | // "z": NOP (no operation) 788 | // (When a "z" is read, the generated JavaScript code will just contain "undefined;") 789 | // Addressing: imp 790 | // Opcode: EA 791 | // Cycles total: 2 792 | // Cycles addr.: 0 793 | // Cycles opc. : 0 794 | + "" 795 | 796 | // Make an array from this string 797 | ).split(";") 798 | 799 | // Fetch the right instruction for the current opcode (ignore every illegal opcode where o % 4 == 3): 800 | // (The string below is optomized for compression: all the illegal opcodes are assigned characters that allow extra repetition) 801 | [ 802 | ( 803 | `PI#PI#KIDPI#=I#PI#1IDPI#` 804 | +`E6%)6%M6$)6%;6%E6%F6%E6%` 805 | +`NB'NB'JB&ZB'@B'NB'4B'NB'` 806 | +`Y90Y90L9*[90>90Y90290Y90` 807 | +`VRSVRS-zXVRSORSVRSWR VRS` 808 | +`/5A/5AQ5U/5A:5A/5AH5T/5A` 809 | +`"7C"7C.7("7C?7C"7C37C"7C` 810 | +`!8+!8+,8z!8+<8+!8+G8+!8+` 811 | )[o - (o >> 2)].charCodeAt() - 32 812 | ] 813 | ) 814 | ); 815 | 816 | // Emulation 817 | // --------- 818 | 819 | // If an interrupt (v = 1/2/3) is specified, it's executed 820 | // Otherwise, execute the next opcode, at the address pointed by the PC register 821 | op = (v, z) => ( 822 | 823 | // - Fetch opcode at address PC (costs 1 cycle), save it in o 824 | // - Increment PC, save it in a 825 | // - Fetch the byte at address a (costs 1 cycle), save it in p 826 | o = r(PC), 827 | p = r(a = PC+1), 828 | 829 | // Execute an interrupt if v is set 830 | v ? ( 831 | 832 | // 1: NMI: 833 | // Push PC and P with B flag set to 0, then set I to 1, 834 | // then jump to address stored at $FFFA-$FFFB 835 | // This costs 7 cycles 836 | // On NES, it only works when VBlank is enabled (bit 7 of PPU register $2000 = 1), otherwise it's skipped and only costs 2 cycles 837 | 838 | // 2: Reset: 839 | // Push PC and P with B flag set to 0, then set I to 1, 840 | // then jump to address stored at $FFFC-$FFFD 841 | // This resets c and costs 8 cycles 842 | // On NES, this also resets the PPU 843 | 844 | // 3: IRQ/BRK: 845 | // Push PC and P with B flag set to 0 (IRQ) or 1 (BRK), then set I to 1, 846 | // then jump to address stored at $FFFE-$FFFF 847 | // This costs 7 cycles 848 | 849 | // (v > 1 || r(0x2000) >> 7) && ( // NES-specific test, commented here 850 | 851 | ( 852 | ( 853 | (v - 2) 854 | ? (h(PC >> 8), h(255 & PC), h(z ? (P|16) : (239 & P))) // NMI/IRQ/BRK 855 | : (S = (S-3) & 255, c = 6) // Reset 856 | ), 857 | 858 | I = 1, 859 | PC = r(65528 + v * 2) + 256 * r(65528 + v * 2 + 1) 860 | ) 861 | 862 | // ) 863 | ) 864 | 865 | // Or execute the next instruction: 866 | : ( 867 | O[o](), 868 | PC++, 869 | PC &= 0xFFFF 870 | ), 871 | 872 | // Update status register P according to the new flags values 873 | P = C + Z*2 + I*4 + D*8 + B*16 + 32 + V*64 + N*128 874 | ) --------------------------------------------------------------------------------