diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2020-11-22 20:38:51 +0100 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2020-11-22 20:38:51 +0100 |
commit | 6c3401b8a2ce7a2dfe21a253f840f286088b1921 (patch) | |
tree | f49b1bba8b10c3b3681927764cd795e7af4ad9e5 | |
parent | 052899e196c6a2660651b9896ceed3313e7d0bac (diff) | |
download | 6502-6c3401b8a2ce7a2dfe21a253f840f286088b1921.tar.gz 6502-6c3401b8a2ce7a2dfe21a253f840f286088b1921.tar.bz2 |
more work on emulator
-rw-r--r-- | LINKS | 8 | ||||
-rw-r--r-- | README | 3 | ||||
-rw-r--r-- | doc/opcodes.html | 1516 | ||||
-rw-r--r-- | emu/6502.c | 10 | ||||
-rw-r--r-- | emu/6502.h | 6 | ||||
-rw-r--r-- | emu/README | 4 | ||||
-rw-r--r-- | emu/emu.c | 13 | ||||
-rw-r--r-- | emu/emul.c | 117 | ||||
-rw-r--r-- | emu/emul.h | 30 |
9 files changed, 1697 insertions, 10 deletions
@@ -9,6 +9,7 @@ https://github.com/dpm-343/6502-monitor/blob/master/6502-monitor.ino other 6052: http://lateblt.tripod.com/6502prj1.htm 74LS138 decoder IC (3to8) +https://www.westerndesigncenter.com/wdc/chips.php XA cross assembler http://www.floodgap.com/retrotech/xa/xa-old.html @@ -36,6 +37,7 @@ https://www.jameco.com/z/6551-Major-Brands-IC-6551-Asynchronous-Communication-In assemblers for 6502: http://sun.hasenbraten.de/vasm/ +https://cc65.github.io/ (ca65) emulators for 6502: https://github.com/alimansfield2016/ATM65C02_EMU, nice idea, c++ code quite bad @@ -86,6 +88,7 @@ https://github.com/Klaus2m5/6502_65C02_functional_tests (format tests and verifi https://hgj.hu/live-migrating-a-virtual-machine-with-libvirt-without-a-shared-storage/ https://hackaday.io/project/174128/logs https://sbc.rictor.org/ +http://retro.hansotten.nl/ delay/timers: https://gist.github.com/superjamie/fd80fabadf39199c97de400213f614e9 @@ -126,3 +129,8 @@ http://www.obelisk.me.uk/6502/ https://sta.c64.org/cbm64mem.html https://www.pagetable.com/?p=410 https://github.com/Klaus2m5/6502_65C02_functional_tests +http://nparker.llx.com/a2/opcodes.html + +testing: +http://www.ucunit.org/_related__sites.html +http://cunit.sourceforge.net/ @@ -105,6 +105,9 @@ switching off/on the counter. 21.11.2020: work on a simple 6502 emulator while watching youtube series +22.11.2020: +more work on emulator + commands -------- diff --git a/doc/opcodes.html b/doc/opcodes.html new file mode 100644 index 0000000..82947ce --- /dev/null +++ b/doc/opcodes.html @@ -0,0 +1,1516 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" +"http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"> + <meta http-equiv="Content-Style-Type" content="text/css"> + <title>The 6502 Instruction Set Decoded</title> + <style type="text/css"><!-- +body { color: black; background: white; font-size: 12pt } +p, h1, h2, h3, td, li { font-family: sans-serif } +a:link { color: #0000EE } +a:visited { color: #551A8B } +table { text-align: center } +.small { font-size: smaller } +.p02 { background: lime } +.pc02 { background: yellow } +.p816 { background: red } + --></style> + </head> + <body> + <p><a href="http://www.llx.com/">LLX</a> > + <a href="/~nparker/">Neil Parker</a> > + <a href="index.html">Apple II</a> > 6502 Instruction Set</p> + <h1>The 6502/65C02/65C816 Instruction Set Decoded</h1> + <h2>Introduction</h2> + <p> + Though the 6502 instruction set has a number of quirks and + irregularities, large portions of it can be broken up into regular + patterns. An understanding of these patterns can be beneficial to + authors of assemblers or disassemblers for 6502 code--for example, the + Apple II ROM uses the information described below to greatly reduce the + size of the instruction tables used by the built-in machine language + disassembler. + </p> + <p> + Note that the discussion below assumes a knowledge of 6502 + programming. If you're looking for a tutorial or general programming + reference for the 6502, I recommend starting at <a + href="http://www.6502.org">6502.org</a>. There are also some useful + documents at <a href="http://www.westerndesigncenter.com">Western + Design Center</a>. + </p> + <ul> + <li><a href="#chart">Instruction Chart</a></li> + <li><a href="#ins02">6502 Instructions</a></li> + <li><a href="#insc02">65C02 Instructions</a></li> + <li><a href="#ins816">65C816 Instructions</a></li> + </ul> + <h2><a name="chart">Instruction Chart</a></h2> + <p> + Shown below are the instructions of the 6502, 65C02, and 65C816 processors. + <span class="p02">GREEN UPPERCASE</span> indicates instructions found on + all processors; <span class="pc02">Yellow Mixed Case</span> indicates + instructions introduced on the 65C02, and <span class="p816">red + lowercase</span> indicates instructions found only on the 65C816. The bit + manipulation instructions found only on the Rockwell and WDC versions of + the 65C02 are not included in the table, nor are the "undocumented" + instructions of the original 6502. (However, after noting the search + engine strings commonly used to locate this page, I have added discussions + of these points below.) + </p> + <table border=1 class="small"> + <tr> + <td> </td> + <td>x0</td> + <td>x1</td> + <td>x2</td> + <td>x3</td> + <td>x4</td> + <td>x5</td> + <td>x6</td> + <td>x7</td> + <td>x8</td> + <td>x9</td> + <td>xA</td> + <td>xB</td> + <td>xC</td> + <td>xD</td> + <td>xE</td> + <td>xF</td> + </tr> + <tr> + <td>0x</td> + <td class="p02">BRK b</td> + <td class="p02">ORA (d,X)</td> + <td class="p816">cop b</td> + <td class="p816">ora d,S</td> + <td class="pc02">Tsb d</td> + <td class="p02">ORA d</td> + <td class="p02">ASL d</td> + <td class="p816">ora [d]</td> + <td class="p02">PHP</td> + <td class="p02">ORA #</td> + <td class="p02">ASL A</td> + <td class="p816">phd</td> + <td class="pc02">Tsb a</td> + <td class="p02">ORA a</td> + <td class="p02">ASL a</td> + <td class="p816">ora al</td> + </tr> + <tr> + <td>1x</td> + <td class="p02">BPL r</td> + <td class="p02">ORA (d),Y</td> + <td class="pc02">Ora (d)</td> + <td class="p816">ora (d,S),Y</td> + <td class="pc02">Trb d</td> + <td class="p02">ORA d,X</td> + <td class="p02">ASL d,X</td> + <td class="p816">ora [d],Y</td> + <td class="p02">CLC</td> + <td class="p02">ORA a,Y</td> + <td class="pc02">Inc A</td> + <td class="p816">tcs</td> + <td class="pc02">Trb a</td> + <td class="p02">ORA a,X</td> + <td class="p02">ASL a,X</td> + <td class="p816">ora al,X</td> + </tr> + <tr> + <td>2x</td> + <td class="p02">JSR a</td> + <td class="p02">AND (d,X)</td> + <td class="p816">jsl al</td> + <td class="p816">and d,S</td> + <td class="p02">BIT d</td> + <td class="p02">AND d</td> + <td class="p02">ROL d</td> + <td class="p816">and [d]</td> + <td class="p02">PLP</td> + <td class="p02">AND #</td> + <td class="p02">ROL A</td> + <td class="p816">pld</td> + <td class="p02">BIT a</td> + <td class="p02">AND a</td> + <td class="p02">ROL a</td> + <td class="p816">and al</td> + </tr> + <tr> + <td>3x</td> + <td class="p02">BMI r</td> + <td class="p02">AND (d),Y</td> + <td class="pc02">And (d)</td> + <td class="p816">and (d,S),Y</td> + <td class="pc02">Bit d,X</td> + <td class="p02">AND d,X</td> + <td class="p02">ROL d,X</td> + <td class="p816">and [d],Y</td> + <td class="p02">SEC</td> + <td class="p02">AND a,Y</td> + <td class="pc02">Dec A</td> + <td class="p816">tsc</td> + <td class="pc02">Bit a,X</td> + <td class="p02">AND a,X</td> + <td class="p02">ROL a,X</td> + <td class="p816">and al,X</td> + </tr> + <tr> + <td>4x</td> + <td class="p02">RTI</td> + <td class="p02">EOR (d,X)</td> + <td class="p816">wdm</td> + <td class="p816">eor d,S</td> + <td class="p816">mvp s,d</td> + <td class="p02">EOR d</td> + <td class="p02">LSR d</td> + <td class="p816">eor [d]</td> + <td class="p02">PHA</td> + <td class="p02">EOR #</td> + <td class="p02">LSR A</td> + <td class="p816">phk</td> + <td class="p02">JMP a</td> + <td class="p02">EOR a</td> + <td class="p02">LSR a</td> + <td class="p816">eor al</td> + </tr> + <tr> + <td>5x</td> + <td class="p02">BVC r</td> + <td class="p02">EOR (d),Y</td> + <td class="pc02">Eor (d)</td> + <td class="p816">eor (d,S),Y</td> + <td class="p816">mvn s,d</td> + <td class="p02">EOR d,X</td> + <td class="p02">LSR d,X</td> + <td class="p816">eor [d],Y</td> + <td class="p02">CLI</td> + <td class="p02">EOR a,Y</td> + <td class="pc02">Phy</td> + <td class="p816">tcd</td> + <td class="p816">jmp al</td> + <td class="p02">EOR a,X</td> + <td class="p02">LSR a,X</td> + <td class="p816">eor al,X</td> + </tr> + <tr> + <td>6x</td> + <td class="p02">RTS</td> + <td class="p02">ADC (d,X)</td> + <td class="p816">per rl</td> + <td class="p816">adc d,S</td> + <td class="pc02">Stz d</td> + <td class="p02">ADC d</td> + <td class="p02">ROR d</td> + <td class="p816">adc [d]</td> + <td class="p02">PLA</td> + <td class="p02">ADC #</td> + <td class="p02">ROR A</td> + <td class="p816">rtl</td> + <td class="p02">JMP (a)</td> + <td class="p02">ADC a</td> + <td class="p02">ROR a</td> + <td class="p816">adc al</td> + </tr> + <tr> + <td>7x</td> + <td class="p02">BVS r</td> + <td class="p02">ADC (d),Y</td> + <td class="pc02">Adc (d)</td> + <td class="p816">adc (d,S),Y</td> + <td class="pc02">Stz d,X</td> + <td class="p02">ADC d,X</td> + <td class="p02">ROR d,X</td> + <td class="p816">adc [d],Y</td> + <td class="p02">SEI</td> + <td class="p02">ADC a,Y</td> + <td class="pc02">Ply</td> + <td class="p816">tdc</td> + <td class="pc02">Jmp (a,X)</td> + <td class="p02">ADC a,X</td> + <td class="p02">ROR a,X</td> + <td class="p816">adc al,X</td> + </tr> + <tr> + <td>8x</td> + <td class="pc02">Bra r</td> + <td class="p02">STA (d,X)</td> + <td class="p816">brl rl</td> + <td class="p816">sta d,S</td> + <td class="p02">STY d</td> + <td class="p02">STA d</td> + <td class="p02">STX d</td> + <td class="p816">sta [d]</td> + <td class="p02">DEY</td> + <td class="pc02">Bit #</td> + <td class="p02">TXA</td> + <td class="p816">phb</td> + <td class="p02">STY a</td> + <td class="p02">STA a</td> + <td class="p02">STX a</td> + <td class="p816">sta al</td> + </tr> + <tr> + <td>9x</td> + <td class="p02">BCC r</td> + <td class="p02">STA (d),Y</td> + <td class="pc02">Sta (d)</td> + <td class="p816">sta (d,S),Y</td> + <td class="p02">STY d,X</td> + <td class="p02">STA d,X</td> + <td class="p02">STX d,Y</td> + <td class="p816">sta [d],Y</td> + <td class="p02">TYA</td> + <td class="p02">STA a,Y</td> + <td class="p02">TXS</td> + <td class="p816">txy</td> + <td class="pc02">Stz a</td> + <td class="p02">STA a,X</td> + <td class="pc02">Stz a,X</td> + <td class="p816">sta al,X</td> + </tr> + <tr> + <td>Ax</td> + <td class="p02">LDY #</td> + <td class="p02">LDA (d,X)</td> + <td class="p02">LDX #</td> + <td class="p816">lda d,S</td> + <td class="p02">LDY d</td> + <td class="p02">LDA d</td> + <td class="p02">LDX d</td> + <td class="p816">lda [d]</td> + <td class="p02">TAY</td> + <td class="p02">LDA #</td> + <td class="p02">TAX</td> + <td class="p816">plb</td> + <td class="p02">LDY a</td> + <td class="p02">LDA a</td> + <td class="p02">LDX a</td> + <td class="p816">lda al</td> + </tr> + <tr> + <td>Bx</td> + <td class="p02">BCS r</td> + <td class="p02">LDA (d),Y</td> + <td class="pc02">Lda (d)</td> + <td class="p816">lda (d,S),Y</td> + <td class="p02">LDY d,X</td> + <td class="p02">LDA d,X</td> + <td class="p02">LDX d,Y</td> + <td class="p816">lda [d],Y</td> + <td class="p02">CLV</td> + <td class="p02">LDA a,Y</td> + <td class="p02">TSX</td> + <td class="p816">tyx</td> + <td class="p02">LDY a,X</td> + <td class="p02">LDA a,X</td> + <td class="p02">LDX a,Y</td> + <td class="p816">lda al,X</td> + </tr> + <tr> + <td>Cx</td> + <td class="p02">CPY #</td> + <td class="p02">CMP (d,X)</td> + <td class="p816">rep #</td> + <td class="p816">cmp d,S</td> + <td class="p02">CPY d</td> + <td class="p02">CMP d</td> + <td class="p02">DEC d</td> + <td class="p816">cmp [d]</td> + <td class="p02">INY</td> + <td class="p02">CMP #</td> + <td class="p02">DEX</td> + <td class="p816">wai</td> + <td class="p02">CPY a</td> + <td class="p02">CMP a</td> + <td class="p02">DEC a</td> + <td class="p816">cmp al</td> + </tr> + <tr> + <td>Dx</td> + <td class="p02">BNE r</td> + <td class="p02">CMP (d),Y</td> + <td class="pc02">Cmp (d)</td> + <td class="p816">cmp (d,S),Y</td> + <td class="p816">pei d</td> + <td class="p02">CMP d,X</td> + <td class="p02">DEC d,X</td> + <td class="p816">cmp [d],Y</td> + <td class="p02">CLD</td> + <td class="p02">CMP a,Y</td> + <td class="pc02">Phx</td> + <td class="p816">stp</td> + <td class="p816">jml (a)</td> + <td class="p02">CMP a,X</td> + <td class="p02">DEC a,X</td> + <td class="p816">cmp al,X</td> + </tr> + <tr> + <td>Ex</td> + <td class="p02">CPX #</td> + <td class="p02">SBC (d,X)</td> + <td class="p816">sep #</td> + <td class="p816">sbc d,S</td> + <td class="p02">CPX d</td> + <td class="p02">SBC d</td> + <td class="p02">INC d</td> + <td class="p816">sbc [d]</td> + <td class="p02">INX</td> + <td class="p02">SBC #</td> + <td class="p02">NOP</td> + <td class="p816">xba</td> + <td class="p02">CPX a</td> + <td class="p02">SBC a</td> + <td class="p02">INC a</td> + <td class="p816">sbc al</td> + </tr> + <tr> + <td>Fx</td> + <td class="p02">BEQ r</td> + <td class="p02">SBC (d),Y</td> + <td class="pc02">Sbc (d)</td> + <td class="p816">sbc (d,S),Y</td> + <td class="p816">pea a</td> + <td class="p02">SBC d,X</td> + <td class="p02">INC d,X</td> + <td class="p816">sbc [d],Y</td> + <td class="p02">SED</td> + <td class="p02">SBC a,Y</td> + <td class="pc02">Plx</td> + <td class="p816">xce</td> + <td class="p816">jsr (a,X)</td> + <td class="p02">SBC a,X</td> + <td class="p02">INC a,X</td> + <td class="p816">sbc al,X</td> + </tr> + </table> + <h2><a name="ins02">6502 Instructions</a></h2> + <p> + Most instructions that explicitly reference memory locations have bit + patterns of the form <b>aaabbbcc</b>. The <b>aaa</b> and <b>cc</b> bits + determine the opcode, and the <b>bbb</b> bits determine the addressing mode. + </p> + <p> + Instructions with <b>cc</b> = <b>01</b> are the most regular, and are + therefore considered first. The <b>aaa</b> bits determine the opcode + as follows: + </p> + <table border=1> + <tr><td><b>aaa</b></td><td>opcode</td></tr> + <tr><td>000</td><td>ORA</td></tr> + <tr><td>001</td><td>AND</td></tr> + <tr><td>010</td><td>EOR</td></tr> + <tr><td>011</td><td>ADC</td></tr> + <tr><td>100</td><td>STA</td></tr> + <tr><td>101</td><td>LDA</td></tr> + <tr><td>110</td><td>CMP</td></tr> + <tr><td>111</td><td>SBC</td></tr> + </table> + <p> + And the addressing mode (<b>bbb</b>) bits: + </p> + <table border=1> + <tr><td><b>bbb</b></td><td>addressing mode</td></tr> + <tr><td>000</td><td>(zero page,X)</td></tr> + <tr><td>001</td><td>zero page</td></tr> + <tr><td>010</td><td>#immediate</td></tr> + <tr><td>011</td><td>absolute</td></tr> + <tr><td>100</td><td>(zero page),Y</td></tr> + <tr><td>101</td><td>zero page,X</td></tr> + <tr><td>110</td><td>absolute,Y</td></tr> + <tr><td>111</td><td>absolute,X</td></tr> + </table> + <p> + Putting it all together: + </p> + <table border=1> + <tr> + <td> </td> + <td>ORA</td> + <td>AND</td> + <td>EOR</td> + <td>ADC</td> + <td>STA</td> + <td>LDA</td> + <td>CMP</td> + <td>SBC</td> + </tr> + <tr> + <td>(zp,X)</td> + <td>01</td> + <td>21</td> + <td>41</td> + <td>61</td> + <td>81</td> + <td>A1</td> + <td>C1</td> + <td>E1</td> + </tr> + <tr> + <td>zp</td> + <td>05</td> + <td>25</td> + <td>45</td> + <td>65</td> + <td>85</td> + <td>A5</td> + <td>C5</td> + <td>E5</td> + </tr> + <tr> + <td>#</td> + <td>09</td> + <td>29</td> + <td>49</td> + <td>69</td> + <td> </td> + <td>A9</td> + <td>C9</td> + <td>E9</td> + </tr> + <tr> + <td>abs</td> + <td>0D</td> + <td>2D</td> + <td>4D</td> + <td>6D</td> + <td>8D</td> + <td>AD</td> + <td>CD</td> + <td>ED</td> + </tr> + <tr> + <td>(zp),Y</td> + <td>11</td> + <td>31</td> + <td>51</td> + <td>71</td> + <td>91</td> + <td>B1</td> + <td>D1</td> + <td>F1</td> + </tr> + <tr> + <td>zp,X</td> + <td>15</td> + <td>35</td> + <td>55</td> + <td>75</td> + <td>95</td> + <td>B5</td> + <td>D5</td> + <td>F5</td> + </tr> + <tr> + <td>abs,Y</td> + <td>19</td> + <td>39</td> + <td>59</td> + <td>79</td> + <td>99</td> + <td>B9</td> + <td>D9</td> + <td>F9</td> + </tr> + <tr> + <td>abs,X</td> + <td>1D</td> + <td>3D</td> + <td>5D</td> + <td>7D</td> + <td>9D</td> + <td>BD</td> + <td>DD</td> + <td>FD</td> + </tr> + </table> + <p> + The only irregularity is the absence of the nonsensical immediate STA + instruction. + </p> + <p> + Next we consider the <b>cc</b> = <b>10</b> instructions. These have a + completely different set of opcodes: + </p> + <table border=1> + <tr><td><b>aaa</b></td><td>opcode</td></tr> + <tr><td>000</td><td>ASL</td></tr> + <tr><td>001</td><td>ROL</td></tr> + <tr><td>010</td><td>LSR</td></tr> + <tr><td>011</td><td>ROR</td></tr> + <tr><td>100</td><td>STX</td></tr> + <tr><td>101</td><td>LDX</td></tr> + <tr><td>110</td><td>DEC</td></tr> + <tr><td>111</td><td>INC</td></tr> + </table> + <p> + The addressing modes are similar to the <b>01</b> case, but not quite + the same: + </p> + <table border=1> + <tr><td><b>bbb</b></td><td>addressing mode</td></tr> + <tr><td>000</td><td>#immediate</td></tr> + <tr><td>001</td><td>zero page</td></tr> + <tr><td>010</td><td>accumulator</td></tr> + <tr><td>011</td><td>absolute</td></tr> + <tr><td>101</td><td>zero page,X</td></tr> + <tr><td>111</td><td>absolute,X</td></tr> + </table> + <p> + Note that <b>bbb</b> = <b>100</b> and <b>110</b> are missing. Also, + with STX and LDX, "zero page,X" addressing becomes "zero page,Y", and + with LDX, "absolute,X" becomes "absolute,Y". + </p> + <p> + These fit together like this: + </p> + <table border=1> + <tr> + <td> </td> + <td>ASL</td> + <td>ROL</td> + <td>LSR</td> + <td>ROR</td> + <td>STX</td> + <td>LDX</td> + <td>DEC</td> + <td>INC</td> + </tr> + <tr> + <td>#</td> + <td> </td> + <td> </td> + <td> </td> + <td> </td> + <td> </td> + <td>A2</td> + <td> </td> + <td> </td> + </tr> + <tr> + <td>zp</td> + <td>06</td> + <td>26</td> + <td>46</td> + <td>66</td> + <td>86</td> + <td>A6</td> + <td>C6</td> + <td>E6</td> + </tr> + <tr> + <td>A</td> + <td>0A</td> + <td>2A</td> + <td>4A</td> + <td>6A</td> + <td> </td> + <td> </td> + <td> </td> + <td> </td> + </tr> + <tr> + <td>abs</td> + <td>0E</td> + <td>2E</td> + <td>4E</td> + <td>6E</td> + <td>8E</td> + <td>AE</td> + <td>CE</td> + <td>EE</td> + </tr> + <tr> + <td>zp,X/zp,Y</td> + <td>16</td> + <td>36</td> + <td>56</td> + <td>76</td> + <td>96</td> + <td>B6</td> + <td>D6</td> + <td>F6</td> + </tr> + <tr> + <td>abs,X/abs,Y</td> + <td>1E</td> + <td>3E</td> + <td>5E</td> + <td>7E</td> + <td> </td> + <td>BE</td> + <td>DE</td> + <td>FE</td> + </tr> + </table> + <p> + Most of the gaps in this table are easy to understand. Immediate mode + makes no sense for any instruction other than LDX, and accumulator mode + for DEC and INC didn't appear until the 65C02. The slots that "STX A" + and "LDX A" would occupy are taken by TXA and TAX respectively, which is + exactly what one would expect. The only inexplicable gap is the + absence of a "STX abs,Y" instruction. + </p> + <p> + Next, the <b>cc</b> = <b>00</b> instructions. Again, the opcodes are + different: + </p> + <table border=1> + <tr><td><b>aaa</b></td><td>opcode</td></tr> + <tr><td>001</td><td>BIT</td></tr> + <tr><td>010</td><td>JMP</td></tr> + <tr><td>011</td><td>JMP (abs)</td></tr> + <tr><td>100</td><td>STY</td></tr> + <tr><td>101</td><td>LDY</td></tr> + <tr><td>110</td><td>CPY</td></tr> + <tr><td>111</td><td>CPX</td></tr> + </table> + <p> + It's debatable whether the JMP instructions belong in this + list...I've included them because they <em>do</em> seem to fit, + provided one considers the indirect JMP a separate opcode rather than + a different addressing mode of the absolute JMP. + </p> + <p> + The addressing modes are the same as the <b>10</b> case, except that + accumulator mode is missing. + </p> + <table border=1> + <tr><td><b>bbb</b></td><td>addressing mode</td></tr> + <tr><td>000</td><td>#immediate</td></tr> + <tr><td>001</td><td>zero page</td></tr> + <tr><td>011</td><td>absolute</td></tr> + <tr><td>101</td><td>zero page,X</td></tr> + <tr><td>111</td><td>absolute,X</td></tr> + </table> + <p> + And here's how they fit together: + </p> + <table border=1> + <tr> + <td> </td> + <td>BIT</td> + <td>JMP</td> + <td>JMP()</td> + <td>STY</td> + <td>LDY</td> + <td>CPY</td> + <td>CPX</td> + </tr> + <tr> + <td>#</td> + <td> </td> + <td> </td> + <td> </td> + <td> </td> + <td>A0</td> + <td>C0</td> + <td>E0</td> + </tr> + <tr> + <td>zp</td> + <td>24</td> + <td> </td> + <td> </td> + <td>84</td> + <td>A4</td> + <td>C4</td> + <td>E4</td> + </tr> + <tr> + <td>abs</td> + <td>2C</td> + <td>4C</td> + <td>6C</td> + <td>8C</td> + <td>AC</td> + <td>CC</td> + <td>EC</td> + </tr> + <tr> + <td>zp,X</td> + <td> </td> + <td> </td> + <td> </td> + <td>94</td> + <td>B4</td> + <td> </td> + <td> </td> + </tr> + <tr> + <td>abs,X</td> + <td> </td> + <td> </td> + <td> </td> + <td> </td> + <td>BC</td> + <td> </td> + <td> </td> + </tr> + </table> + <p> + Some of the gaps in this table are understandable (e.g. the lack of an + immediate mode for JMP, JMP(), and STY), but others are not + (e.g. the absence of "zp,X" for CPY and CPX, and the absence of "abs,X" + for STY, CPY, and CPX). Note that if accumulator mode (<b>bbb</b> = + <b>010</b>) were available, "LDY A" would be A8, which falls in the + slot occupied by TAY, but the pattern breaks down elsewhere--TYA is 98, + rather than 88, which we would expect it to be if it corresponded to + the nonexistant "STY A". + </p> + <p> + No instructions have the form <b>aaabbb11</b>. + </p> + <p> + The conditional branch instructions all have the form <b>xxy10000</b>. + The flag indicated by <b>xx</b> is compared with <b>y</b>, and the + branch is taken if they are equal. + </p> + <table border=1> + <tr><td><b>xx</b></td><td>flag</td></tr> + <tr><td>00</td><td>negative</td></tr> + <tr><td>01</td><td>overflow</td></tr> + <tr><td>10</td><td>carry</td></tr> + <tr><td>11</td><td>zero</td></tr> + </table> + <p> + This gives the following branches: + </p> + <table border=1> + <tr><td>BPL</td><td>BMI</td><td>BVC</td><td>BVS</td><td>BCC</td><td>BCS</td> + <td>BNE</td><td>BEQ</td></tr> + <tr><td>10</td><td>30</td><td>50</td><td>70</td><td>90</td><td>B0</td> + <td>D0</td><td>F0</td> + </table> + <p> + The remaining instructions are probably best considered simply by + listing them. Here are the interrupt and subroutine instructions: + </p> + <table border=1> + <tr><td>BRK</td><td>JSR abs</td><td>RTI</td><td>RTS</td></tr> + <tr><td>00</td><td>20</td><td>40</td><td>60</td> + </table> + <p> + (JSR is the only absolute-addressing instruction that doesn't fit the + <b>aaabbbcc</b> pattern.) + </p> + <p> + Other single-byte instructions: + </p> + <table border=1 style="margin-bottom: 10px"> + <tr> + <td>PHP</td> + <td>PLP</td> + <td>PHA</td> + <td>PLA</td> + <td>DEY</td> + <td>TAY</td> + <td>INY</td> + <td>INX</td> + </tr> + <tr> + <td>08</td> + <td>28</td> + <td>48</td> + <td>68</td> + <td>88</td> + <td>A8</td> + <td>C8</td> + <td>E8</td> + </tr> + </table> + <table border=1 style="margin-bottom: 10px"> + <tr> + <td>CLC</td> + <td>SEC</td> + <td>CLI</td> + <td>SEI</td> + <td>TYA</td> + <td>CLV</td> + <td>CLD</td> + <td>SED</td> + </tr> + <tr> + <td>18</td> + <td>38</td> + <td>58</td> + <td>78</td> + <td>98</td> + <td>B8</td> + <td>D8</td> + <td>F8</td> + </tr> + </table> + <table border=1> + <tr> + <td>TXA</td> + <td>TXS</td> + <td>TAX</td> + <td>TSX</td> + <td>DEX</td> + <td>NOP</td> + </tr> + <tr> + <td>8A</td> + <td>9A</td> + <td>AA</td> + <td>BA</td> + <td>CA</td> + <td>EA</td> + </tr> + </table> + <h3>Instruction timing</h3> + <p> + The time required for 6502 instruction to execute is regular and + predictable. The primary rule is this: + </p> + <blockquote> + <p> + Each byte read from or written to memory requires one clock cycle. + </p> + </blockquote> + <p> + However, that simple rule doesn't account for everything--there are a + number of cases when instructions require more clock cycles than the + one-cycle-per-byte rule indicates. (Most of these exceptions arise + because the next "real" memory access can't occur until some internal + operation finishes first.) + </p> + <ul> + <li>All single-byte instructions waste a cycle reading and ignoring the + byte that comes immediately after the instruction (this means no + instruction can take less than two cycles).</li> + <li>Zero page,X, zero page,Y, and (zero page,X) addressing modes spend an + extra cycle reading the unindexed zero page address.</li> + <li>Absolute,X, absolute,Y, and (zero page),Y addressing modes need an + extra cycle if the indexing crosses a page boundary, or if the instruction + writes to memory.</li> + <li>The conditional branch instructions require an extra cycle if the branch + actually happens, and a second extra cycle if the branch happens and + crosses a page boundary.</li> + <li>Read-modify-write instruction (ASL, DEC, INC, LSR, ROL, ROR) need a + cycle for the modify stage (except in accumulator mode, which doesn't + access memory).</li> + <li>Instructions that pull data off the stack (PLA, PLP, RTI, RTS) need an + extra cycle to increment the stack pointer (because the stack pointer + points to the first empty address on the stack, not the last used + address).</li> + <li>RTS needs an extra cycle (in addition to the single-byte penalty and + the pull-from-stack penalty) to increment the return address.</li> + <li>JSR spends an extra cycle juggling the return address internally.</li> + <li>Hardware interrupts take the same number of cycles as a BRK + instruction (even in the case of RESET, which goes through the motions of + pushing the return address and status on the stack, but doesn't actually + alter the stack).</li> + </ul> + <h3>"Undocumented" 6502 instructions</h3> + <p> + The above-described instructions (the ones shown in <span + class="p02">GREEN UPPERCASE</span> in the table at the top of this + page) are the only ones documented in any manufacturer's official data + sheets. The question often arises, "What do all those other leftover + bytes do if you try to execute them as instructions?" + <p> + In general the behavior of instructions other than those listed above + cannot be described exactly, as they tend to be somewhat unstable, and + do not always behave the same way on chips made by different + manufacturers, and some instructions don't even behave the same way + twice on the same chip. Those looking for a precise listing of + "undocumented" instruction behaviors will have to look elsewhere, and + should beware that the behaviors described on other web pages may be + specific to 6502s made by a particular (often unspecified) manufacturer. + </p> + <p> + However, there are some facts that seem to be common across all 6502s. + The most insteresting case is the <b>cc</b> = <b>11</b> instructions: + these execute the adjacent <b>cc</b> = <b>01</b> and <b>cc</b> = + <b>10</b> instructions <em>simultaneously</em>. For example, <b>AF</b> + executes <b>AD</b> ("LDA absolute") and <b>AE</b> ("LDX absolute") at + the same time, putting the same value in both the accumulator and the X + register. + </p> + <p> + In some cases the <b>01</b> and <b>10</b> instructions are + incompatible. For example, <b>8F</b> executes <b>8D</b> ("STA + absolute") and <b>8E</b> ("STX absolute") at the same time. So which + register actually gets written to memory? Usually some mixture of the + two, in a manner that varies depending on who made the 6502, when it was + made, the phase of the moon, and other unpredictable variables. + </p> + <p> + The behavior of the <b>11</b> instructions is especially problematic in + those cases where the adjacent <b>01</b> or <b>10</b> instruction is also + undocumented. Sometimes you can get a partial idea of what happens by + looking at what the missing <b>01</b> or <b>10</b> instruction would be + if that opcode/addressing mode combination weren't missing. + <b>Xxxx1011</b> instructions are also problematic--some of these seem + to mix not only the adjacent <b>01</b> and <b>10</b> instructions, but + also the immediate mode of the corresponding <b>10</b> instruction. + </p> + <p> + Most of the missing <b>00</b>, <b>01</b>, and <b>10</b> instructions + seem to behave like NOPs, but using the addressing mode indicated by + the <b>bbb</b> bits. But apparently this isn't always reliable--there + are reports of some of these instructions occasionally locking up the + processor. + </p> + <p> + Instructions of the form <b>xxxx0010</b> usually lock up the processor, + so that a reset is required to recover. The instructions <b>82</b>, + <b>C2</b>, and <b>E2</b> (corresponding to the nonexistant immediate + mode of STX, DEC, and INC) may sometimes behave as two-byte NOPs, but don't + count on it. + </p> + <h2><a name="insc02">65C02 Instructions</a></h2> + <p> + The new instructions of the 65C02 are much less logical than those + listed above. The designers of the 65C02 apparently chose to continue + leaving the <b>cc</b> = <b>11</b> instructions empty, and this didn't + leave much space for new instructions. Some instructions landed in + logical places, but others had to be assigned wherever there was room, + whether it made sense or not. + </p> + <p> + The new zero-page indirect addressing mode fills the previously-unused + <b>bbb</b> = <b>100</b> slot of the <b>cc</b> = <b>10</b> instructions, + but the opcodes are those of the <b>cc</b> = <b>01</b> instructions. + </p> + <table border=1> + <tr> + <td> </td> + <td>ORA</td> + <td>AND</td> + <td>EOR</td> + <td>ADC</td> + <td>STA</td> + <td>LDA</td> + <td>CMP</td> + <td>SBC</td> + </tr> + <tr> + <td>(zp)</td> + <td>12</td> + <td>32</td> + <td>52</td> + <td>72</td> + <td>92</td> + <td>B2</td> + <td>D2</td> + <td>F2</td> + </tr> + </table> + <p> + "JMP (abs,X)" is right where it ought to be (<b>011 111 00</b>), if one + continues to regard the indirect JMP as a separate opcode from the + absolute JMP: + </p> + <table border=1> + <tr> + <td> </td> + <td>JMP()</td> + </tr> + <tr> + <td>abs,X</td> + <td>7C</td> + </tr> + </table> + <p> + "BIT zp,X" and "BIT abs,X" ended up exactly where one would expect them + to be, but "BIT #" had to be moved because its slot was already taken by + JSR: + </p> + <table border=1> + <tr> + <td> </td> + <td>BIT</td> + </tr> + <tr> + <td>#</td> + <td>89</td> + </tr> + <tr> + <td>zp,X</td> + <td>34</td> + </tr> + <tr> + <td>abs,X</td> + <td>3C</td> + </tr> + </table> + <p> + TSB ended up in a reasonable place (<b>000bbb00</b>): + </p> + <table border=1> + <tr> + <td> </td> + <td>TSB</td> + </tr> + <tr> + <td>zp</td> + <td>04</td> + </tr> + <tr> + <td>abs</td> + <td>0C</td> + </tr> + </table> + <p> + But the above assigments exhaust the logical possibilities for + opcodes that explicity reference memory locations, so TRB and STZ had to + be put wherever room could be found: + </p> + <table border=1> + <tr> + <td> </td> + <td>TRB</td> + <td>STZ</td> + </tr> + <tr> + <td>zp</td> + <td>14</td> + <td>64</td> + </tr> + <tr> + <td>abs</td> + <td>1C</td> + <td>9C</td> + </tr> + <tr> + <td>zp,X</td> + <td> </td> + <td>74</td> + </tr> + <tr> + <td>abs,X</td> + <td> </td> + <td>9E</td> + </tr> + </table> + <p> + That leaves the relative branch instruction + </p> + <table border=1> + <tr> + <td>BRA</td> + </tr> + <tr> + <td>80</td> + </tr> + </table> + <p> + and the single-byte instructions: + </p> + <table border=1> + <tr> + <td>INC A</td> + <td>DEC A</td> + <td>PHY</td> + <td>PLY</td> + <td>PHX</td> + <td>PLX</td> + </tr> + <tr> + <td>1A</td> + <td>3A</td> + <td>5A</td> + <td>7A</td> + <td>DA</td> + <td>FA</td> + </tr> + </table> + <h3>Additional instructions found on some 65C02s</h3> + <p> + Actually, I lied when I said above that the designers of the 65C02 + chose to leave the <b>cc</b> = <b>11</b> instructions unused. On + 65C02s made by Rockwell and by WDC, some of these instructions are used + for additional bit setting, clearing, and testing instructions. These + instructions are missing on 65C02s made by other manufacturers. (And + since this page is part of a set of Apple II-related pages, I should + point out that Apple never shipped any computers that used Rockwell or + WDC 65C02s, so none of the instructions in this section are available + on an unmodified Apple II.) + </p> + <p> + The bit set and clear instructions have the form <b>xyyy0111</b>, + where <b>x</b> is 0 to clear a bit or 1 to set it, and <b>yyy</b> is + which bit at the memory location to set or clear. + </p> + <table border=1 style="margin-bottom: 10px"> + <tr> + <td> </td> + <td>RMB0</td> + <td>RMB1</td> + <td>RMB2</td> + <td>RMB3</td> + <td>RMB4</td> + <td>RMB5</td> + <td>RMB6</td> + <td>RMB7</td> + </tr> + <tr> + <td>zp</td> + <td>07</td> + <td>17</td> + <td>27</td> + <td>37</td> + <td>47</td> + <td>57</td> + <td>67</td> + <td>77</td> + </tr> + </table> + <table border=1> + <tr> + <td> </td> + <td>SMB0</td> + <td>SMB1</td> + <td>SMB2</td> + <td>SMB3</td> + <td>SMB4</td> + <td>SMB5</td> + <td>SMB6</td> + <td>SMB7</td> + </tr> + <tr> + <td>zp</td> + <td>87</td> + <td>97</td> + <td>A7</td> + <td>B7</td> + <td>C7</td> + <td>D7</td> + <td>E7</td> + <td>F7</td> + </tr> + </table> + <p> + Similarly, the test-and-branch instructions are of the form + <b>xyyy1111</b>, where <b>x</b> is 0 to test whether the bit is 0, or + 1 to test whether it is 1, and <b>yyy</b> is which bit to test. + </p> + <table border=1 style="margin-bottom: 10px"> + <tr> + <td> </td> + <td>BBR0</td> + <td>BBR1</td> + <td>BBR2</td> + <td>BBR3</td> + <td>BBR4</td> + <td>BBR5</td> + <td>BBR6</td> + <td>BBR7</td> + </tr> + <tr> + <td>zp,rel</td> + <td>0F</td> + <td>1F</td> + <td>2F</td> + <td>3F</td> + <td>4F</td> + <td>5F</td> + <td>6F</td> + <td>7F</td> + </tr> + </table> + <table border=1> + <tr> + <td> </td> + <td>BBS0</td> + <td>BBS1</td> + <td>BBS2</td> + <td>BBS3</td> + <td>BBS4</td> + <td>BBS5</td> + <td>BBS6</td> + <td>BBS7</td> + </tr> + <tr> + <td>zp,rel</td> + <td>8F</td> + <td>9F</td> + <td>AF</td> + <td>BF</td> + <td>CF</td> + <td>DF</td> + <td>EF</td> + <td>FF</td> + </tr> + </table> + <p> + Additionally, the WDC version of the 65C02 includes the 65C816's STP and + WAI instructions (see below). + </p> + <h3>"Undocumented" 65C02 instructions</h3> + <p> + There aren't really any undocumented instructions on the 65C02--any + instructions not listed above are documented as performing no + operation. + </p> + <p> + However, these alternate NOPs are not created equal. Some have one- or + two-byte operands (which they don't do anything with), and they take + different amounts of time to execute. + </p> + <table border=1> + <tr><td>Instruction</td><td>Bytes</td><td>Cycles</td></tr> + <tr><td>xxxxxx10</td><td>2</td><td>2</td></tr> + <tr><td>xxxxxx11</td><td>1</td><td>1</td></tr> + <tr><td>01000100</td><td>2</td><td>3</td></tr> + <tr><td>x1x10100</td><td>2</td><td>4</td></tr> + <tr><td>01011100</td><td>3</td><td>8</td></tr> + <tr><td>11x11100</td><td>3</td><td>4</td></tr> + </table> + <p>Actually, it's not quite correct to say that these instructions don't do + anything with their operands. A memory read <em>does</em> occur, + generally using the addressing mode you would expect from the bit + patterns--which may be significant if there happens to be a memory-mapped + hardware device at the target address.</p> + <p>Correspondent <a href="https://laughtonelectronics.com/">Jeff Laughton</a> + reports that the behavior of 01011100 (5C) is strange: "5C bb aa", after + fetching its three bytes, accesses FFbb, and then spends four cycles + accessing FFFF.</p> + <h3>Instruction timing</h3> + <p> + Generally, instruction timing on the 65C02 is the same as on the 6502. + There are, however, a few exceptions: + </p> + <ul> + <li>As shown above, the "undocumented" <b>cc = 11</b> instructions don't + pay the one-cycle penalty that other single-byte instructions pay.</li> + <li>If the decimal flag is set, ADC and SBC spend an additional cycle + making the N and Z flags reflect the decimal result instead of the binary + result (this fixes the famous 6502 decimal bug).</li> + <li>"JMP (absolute)" spends an extra cycle making sure the high byte of the + indirect address is correct (this fixes the famous 6502 indirect jump + bug).</li> + <li>"JMP (absolute,X)" spends a cycle doing the indexing. It <i>doesn't</i> + need to spend an additional cycle getting the high byte right.</li> + <li>ASL, LSR, ROL, and ROR, in absolute,X addressing mode, don't pay the + one-cycle penalty that other write instruction pay, unless the indexing + crosses a page boundary. INC and DEC <em>do</em> pay the penalty. + (The manufacturer data sheets are inconsistent, and often wrong, about + this).</li> + <li>BBRx and BBSx spend one more cycle than expected (probably doing the + bit test).</li> + </ul> + <p> + Note that BRA is an ordinary branch instruction that always branches. + </p> + <h2><a name="ins816">65C816 Instructions</a></h2> + <p> + The 65C816 uses the <b>cc</b> = <b>11</b> instructions, but not for + Rockwell bit-manipulation opcodes. + Most of these are put to work supplying the new long addressing modes + of the 65C816: + </p> + <table border=1> + <tr><td><b>bbb</b></td><td>addressing mode</td></tr> + <tr><td>000</td><td>offset,S</td></tr> + <tr><td>001</td><td>[direct page]</td></tr> + <tr><td>011</td><td>absolute long</td></tr> + <tr><td>100</td><td>(offset,S),Y</td></tr> + <tr><td>101</td><td>[direct page],Y</td></tr> + <tr><td>111</td><td>absolute long,X</td></tr> + </table> + <p> + These combine with the <b>01</b> opcodes: + </p> + <table border=1> + <tr> + <td> </td> + <td>ORA</td> + <td>AND</td> + <td>EOR</td> + <td>ADC</td> + <td>STA</td> + <td>LDA</td> + <td>CMP</td> + <td>SBC</td> + </tr> + <tr> + <td>d,S</td> + <td>03</td> + <td>23</td> + <td>43</td> + <td>63</td> + <td>83</td> + <td>A3</td> + <td>C3</td> + <td>E3</td> + </tr> + <tr> + <td>[dp]</td> + <td>07</td> + <td>27</td> + <td>47</td> + <td>67</td> + <td>87</td> + <td>A7</td> + <td>C7</td> + <td>E7</td> + </tr> + <tr> + <td>al</td> + <td>0F</td> + <td>2F</td> + <td>4F</td> + <td>6F</td> + <td>8F</td> + <td>AF</td> + <td>CF</td> + <td>EF</td> + </tr> + <tr> + <td>(d,S),Y</td> + <td>13</td> + <td>33</td> + <td>53</td> + <td>73</td> + <td>93</td> + <td>B3</td> + <td>D3</td> + <td>F3</td> + </tr> + <tr> + <td>[dp],Y</td> + <td>17</td> + <td>37</td> + <td>57</td> + <td>77</td> + <td>97</td> + <td>B7</td> + <td>D7</td> + <td>F7</td> + </tr> + <tr> + <td>al,X</td> + <td>1F</td> + <td>3F</td> + <td>5F</td> + <td>7F</td> + <td>9F</td> + <td>BF</td> + <td>DF</td> + <td>FF</td> + </tr> + </table> + <p> + The missing <b>010</b> and <b>110</b> instructions are all single-byte + instructions: + </p> + <table border=1 style="margin-bottom: 10px"> + <tr> + <td>PHD</td> + <td>PLD</td> + <td>PHK</td> + <td>RTL</td> + <td>PHB</td> + <td>PLB</td> + <td>WAI</td> + <td>XBA</td> + </tr> + <tr> + <td>0B</td> + <td>2B</td> + <td>4B</td> + <td>6B</td> + <td>8B</td> + <td>AB</td> + <td>CB</td> + <td>EB</td> + </tr> + </table> + <table border=1> + <tr> + <td>TCS</td> + <td>TSC</td> + <td>TCD</td> + <td>TDC</td> + <td>TXY</td> + <td>TYX</td> + <td>STP</td> + <td>XCE</td> + </tr> + <tr> + <td>1B</td> + <td>3B</td> + <td>5B</td> + <td>7B</td> + <td>9B</td> + <td>BB</td> + <td>DB</td> + <td>FB</td> + </tr> + </table> + <p> + The remaining instructions are a grab-bag assigned to the few remaining + unused positions: + </p> + <table border=1> + <tr><td>COP sig</td><td>02</td></tr> + <tr><td>JSL al</td><td>22</td></tr> + <tr><td>WDM</td><td>42</td></tr> + <tr><td>PER rl</td><td>62</td></tr> + <tr><td>BRL rl</td><td>82</td></tr> + <tr><td>REP #</td><td>C2</td></tr> + <tr><td>SEP #</td><td>E2</td></tr> + <tr><td>MVP sb,db</td><td>44</td></tr> + <tr><td>MVN sb,db</td><td>54</td></tr> + <tr><td>PEI dp</td><td>D4</td></tr> + <tr><td>PEA abs</td><td>F4</td></tr> + <tr><td>JMP al</td><td>5C</td></tr> + <tr><td>JML (abs)</td><td>DC</td></tr> + <tr><td>JSR (abs,X)</td><td>FC</td></tr> + </table> + <h3>Instruction timing</h3> + <p> + The 65816 generally follows 6502 timing rather than 65C02 timing...indeed, + in emulation mode when the direct page register is page-aligned, all 6502 + instructions take the same number of cycles as on the original 6502. New + 65C02 instructions take their 65C02 times. (The 6502 decimal mode and + indirect jump bugs are fixed without requiring the extra cycle that the + 65C02 requires.)</p> + <p>In native mode, the key point to remember is that 16-bit quantities are + transferred between the CPU and memory one 8-bit byte at a time.</p> + <p>Other 65816-specific exceptions:</p> + <ul> + <li>When the direct page register is <i>not</i> page-aligned, all addressing + modes that involve the direct page require an additional cycle.</li> + <li>Absolute,X, absolute,Y, and (direct page),Y addressing modes + <i>always</i> require an extra cycle in 16-bit-index mode, even if the + instruction isn't a write and there isn't page crossing.</li> + <li>Offset,S and (offset,S),Y addressing modes spend a cycle computing the + offset into the stack. (Offset,S),Y spends an additional cycle doing the + indexing.</li> + <li>Conditional branches don't require an extra cycle in native mode if + the branch is taken across a page boundary.</li> + <li>BRL <i>never</i> requires an extra cycle for crossing a page + boundary (but the extra cycle for a branch taken <i>does</i> occur).</li> + <li>PER spends a cycle computing the destination address.</li> + <li>"JSR (absolute,X)" spends a cycle doing the indexing.</li> + <li>JSL spends a cycle juggling the return address internally.</li> + <li>MVN and MVP reread the opcode and operand bytes each pass through the + loop, and spend two additional cycles on each pass moving the PC backwards + to reread the instruction (these two cycles occur even on the last pass, + when the PC isn't actually moved back).</li> + <li>REP, SEP, STP, WAI, and XBA spend a cycle on internal operations. In + the case of STP and WAI, the extra cycle occurs before the CPU stops.</li> + </ul> + <p><a href="http://www.llx.com/">LLX</a> > + <a href="/~nparker/">Neil Parker</a> > + <a href="index.html">Apple II</a> > 6502 Instruction Set</p> + <p> + <small>Original: July 27, 2004<br> + Modified: May 3, 2005<br> + Modified: May 29, 2016--Added a new note about 65C02 "undocumented" + opcodes<br> + Modified: February 24, 2019--Added information about instruction timing<br> + Modified: April 4, 2019--Added missing info about 65618 REP and SEP + timing<br> + Modified: November 19, 2020--Noted that on the 65C02, INC and DEC don't have + the timing optimization that other read-modify-write instructions + have</small> + </p> + <p><small>Thanks to Jeff Laughton for several helpful comments.</small></p> + </body> +</html> @@ -76,9 +76,9 @@ void cpu_6502_write_word( cpu_6502_t *cpu, uint16_t addr, uint16_t data ) cpu->memory->write( cpu->memory, addr + 1, ( data && 0x00FF ) ); } -void cpu_6502_run( cpu_6502_t *cpu ) +void cpu_6502_run( cpu_6502_t *cpu, int steps ) { - while( true ) { + for( int i = 0; i < steps; i++ ) { cpu_6502_step( cpu ); } } @@ -138,7 +138,7 @@ static bool is_carry( cpu_6502_t *cpu ) void cpu_6502_print_state( cpu_6502_t *cpu, uint8_t opcode ) { - fprintf( stderr, "PC: %04X SP: 01%02X PS: %02X %c%c-%c%c%c%c%c A: %02X X: %02X Y: %02X OP: %02X\n", + fprintf( stderr, "PC: %04X SP: 01%02X PS: %02X %c%c-%c%c%c%c%c A: %02X X: %02X Y: %02X OP: %02X steps: %d\n", cpu->PC, cpu->SP, cpu->PS, is_negative( cpu ) ? 'N' : 'n', is_overflow( cpu ) ? 'V' : 'v', @@ -147,7 +147,7 @@ void cpu_6502_print_state( cpu_6502_t *cpu, uint8_t opcode ) is_interrupt( cpu ) ? 'I' : 'i', is_zero( cpu ) ? 'Z' : 'z', is_carry( cpu ) ? 'C' : 'c', - cpu->A, cpu->X, cpu->Y, opcode ); + cpu->A, cpu->X, cpu->Y, opcode, cpu->steps ); } static void update_negative_and_sign( cpu_6502_t *cpu, uint8_t x ) @@ -253,4 +253,6 @@ void cpu_6502_step( cpu_6502_t *cpu ) fprintf( stderr, "ERROR: Illegal opcode %02X at PC %04X\n", opcode, cpu->PC ); exit( EXIT_FAILURE ); } + + cpu->steps++; } @@ -16,6 +16,8 @@ typedef struct struct memory_t *memory; bool debug; + + int steps; } cpu_6502_t; enum { @@ -50,11 +52,11 @@ void cpu_6502_push_byte( cpu_6502_t *cpu, uint8_t data ); void cpu_6502_push_word( cpu_6502_t *cpu, uint16_t data ); uint8_t cpu_6502_pop_byte( cpu_6502_t *cpu ); uint16_t cpu_6502_pop_word( cpu_6502_t *cpu ); -void cpu_6502_run( cpu_6502_t *cpu ); +void cpu_6502_run( cpu_6502_t *cpu, int steps ); +void cpu_6502_step( cpu_6502_t *cpu ); void cpu_6502_print_state( cpu_6502_t *cpu, uint8_t opcode ); void cpu_6502_print_memory( cpu_6502_t *cpu, uint16_t base, uint8_t size ); void cpu_6502_print_stack( cpu_6502_t *cpu ); void cpu_6502_print_zerop_page( cpu_6502_t *cpu ); -void cpu_6502_step( cpu_6502_t *cpu ); #endif diff --git a/emu/README b/emu/README new file mode 100644 index 0000000..fc23a5d --- /dev/null +++ b/emu/README @@ -0,0 +1,4 @@ +emulator for homebrew 6502 based on ben eater ben6502 + +gcc -DWITH_GUI -I/usr/include/SDL2 -lSDL2 -g -Wall -Os -o emu emu.c 6502.c memory.c emul.c + @@ -6,16 +6,23 @@ int main( int argc, char *argv[] ) { + emul_t emul; cpu_6502_t cpu; memory_t memory; - + memory_init( &memory ); memory_load( &memory, ROM_START, ROM_SIZE, "./rom.bin" ); cpu_6502_init( &cpu, &memory ); - cpu.debug = true; + //cpu.debug = true; cpu_6502_reset( &cpu ); - cpu_6502_run( &cpu ); + emul_init( &emul, &cpu, &memory ); + emul.gui = true; + emul_start( &emul ); + + emul_run( &emul ); + + emul_free( &emul ); exit( EXIT_SUCCESS ); } diff --git a/emu/emul.c b/emu/emul.c new file mode 100644 index 0000000..dc7453b --- /dev/null +++ b/emu/emul.c @@ -0,0 +1,117 @@ +#include "emul.h" + +#include <stdio.h> +#include <stdlib.h> + +void emul_init( emul_t *emul, cpu_6502_t *cpu, memory_t *memory ) +{ + emul->cpu = cpu; + emul->memory = memory; + emul->gui = false; +} + +void emul_start( emul_t *emul ) +{ + if( emul->gui ) { +#ifdef WITH_GUI + int rt = SDL_Init( SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO ); + if( rt < 0 ) { + fprintf( stderr, "ERROR: SDL_Init failed: %s\n", SDL_GetError( ) ); + exit( EXIT_FAILURE ); + } + atexit( SDL_Quit ); + SDL_ShowCursor( SDL_ENABLE ); + + int display = -1; + SDL_Rect display_rect; + for( int i = 0; i < SDL_GetNumVideoDisplays( ); i++ ) { + SDL_Rect rect; + if( SDL_GetDisplayBounds( i, &rect ) == 0 ) { + fprintf( stderr, "INFO: display %d has dimensions %dx%d\n", + i, rect.w, rect.h ); + display = i; + display_rect = rect; + } else { + fprintf( stderr, "ERROR: SDL_GetDisplayBounds failed: %s\n", SDL_GetError( ) ); + exit( EXIT_FAILURE ); + } + } + if( display < 0 ) { + fprintf( stderr, "ERROR: no video display found\n" ); + exit( EXIT_FAILURE ); + } + + emul->window = SDL_CreateWindow( "6502 emu", + SDL_WINDOWPOS_UNDEFINED_DISPLAY( display ), + SDL_WINDOWPOS_UNDEFINED_DISPLAY( display ), + 600, 250, 0 ); + if( emul->window == NULL ) { + fprintf( stderr, "ERROR: SDL_CreateWindow failed: %s\n", SDL_GetError( ) ); + exit( EXIT_FAILURE ); + } + + emul->renderer = SDL_CreateRenderer( emul->window, -1, 0 ); + if( emul->renderer == NULL ) { + fprintf( stderr, "ERROR: SDL_Renderer failed: %s\n", SDL_GetError( ) ); + exit( EXIT_FAILURE ); + } + + SDL_ShowWindow( emul->window ); + SDL_SetRenderDrawColor( emul->renderer, 0, 0, 0, 255 ); + SDL_RenderClear( emul->renderer ); + + emul->background_image = SDL_LoadBMP( "../other/breadboard.bmp" ); + emul->background_texture = SDL_CreateTextureFromSurface( emul->renderer, emul->background_image ); + SDL_RenderCopy( emul->renderer, emul->background_texture, NULL, NULL ); + + SDL_RenderPresent( emul->renderer ); +#else + fprintf( stderr, "WARN: gui enabled and not compiled with WITH_GUI (SDL2)\n" ); +#endif + } +} + +void emul_run( emul_t *emul ) +{ +#ifdef WITH_GUI + if( emul->gui ) { + SDL_Event event; + bool done = false; + while( !done ) { + uint32_t frame_start = SDL_GetTicks( ); + + SDL_PollEvent( &event ); + + switch( event.type ) { + case SDL_QUIT: + done = true; + break; + } + + cpu_6502_run( emul->cpu, CPU_FREQUENCY / DISPLAY_FPS ); + + cpu_6502_print_state( emul->cpu, 0 ); + SDL_RenderCopy( emul->renderer, emul->background_texture, NULL, NULL ); + SDL_RenderPresent( emul->renderer ); + + uint32_t frame_end = SDL_GetTicks( ); + int delay = frame_start + 1000 / DISPLAY_FPS - frame_end; + if( delay > 0 ) { + SDL_Delay( delay ); + } + } + } else { + cpu_6502_run( emul->cpu, 100 ); + } +#else + cpu_6502_run( emul->cpu, 100 ); +#endif +} + +void emul_free( emul_t *emul ) +{ + SDL_DestroyTexture( emul->background_texture ); + SDL_FreeSurface( emul->background_image ); + SDL_DestroyRenderer( emul->renderer ); + SDL_DestroyWindow( emul->window ); +} @@ -1,9 +1,37 @@ #ifndef EMUL_H #define EMUL_H +#include "6502.h" +#include "memory.h" + +#include <stdbool.h> + +#ifdef WITH_GUI +#include <SDL.h> +#endif + enum { ROM_START = 0xF800, - ROM_SIZE = 2048 + ROM_SIZE = 2048, + CPU_FREQUENCY = 1000000, + DISPLAY_FPS = 25 }; +typedef struct emul_t { + cpu_6502_t *cpu; + memory_t *memory; + bool gui; +#ifdef WITH_GUI + SDL_Window *window; + SDL_Renderer *renderer; + SDL_Surface *background_image; + SDL_Texture *background_texture; +#endif +} emul_t; + +void emul_init( emul_t *emul, cpu_6502_t *cpu, memory_t *memory ); +void emul_start( emul_t *emul ); +void emul_run( emul_t *emul ); +void emul_free( emul_t *emul ); + #endif |