From 6c3401b8a2ce7a2dfe21a253f840f286088b1921 Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sun, 22 Nov 2020 20:38:51 +0100 Subject: more work on emulator --- LINKS | 8 + README | 3 + doc/opcodes.html | 1516 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ emu/6502.c | 10 +- emu/6502.h | 6 +- emu/README | 4 + emu/emu.c | 13 +- emu/emul.c | 117 +++++ emu/emul.h | 30 +- 9 files changed, 1697 insertions(+), 10 deletions(-) create mode 100644 doc/opcodes.html create mode 100644 emu/README create mode 100644 emu/emul.c diff --git a/LINKS b/LINKS index b5eff15..68ce8c0 100644 --- a/LINKS +++ b/LINKS @@ -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/ diff --git a/README b/README index 483b070..c67574d 100644 --- a/README +++ b/README @@ -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 @@ + + + + + + The 6502 Instruction Set Decoded + + + +

LLX > + Neil Parker > + Apple II > 6502 Instruction Set

+

The 6502/65C02/65C816 Instruction Set Decoded

+

Introduction

+

+ 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. +

+

+ 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 6502.org. There are also some useful + documents at Western + Design Center. +

+ +

Instruction Chart

+

+ Shown below are the instructions of the 6502, 65C02, and 65C816 processors. + GREEN UPPERCASE indicates instructions found on + all processors; Yellow Mixed Case indicates + instructions introduced on the 65C02, and red + lowercase 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.) +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 x0x1x2x3x4x5x6x7x8x9xAxBxCxDxExF
0xBRK bORA (d,X)cop bora d,STsb dORA dASL dora [d]PHPORA #ASL AphdTsb aORA aASL aora al
1xBPL rORA (d),YOra (d)ora (d,S),YTrb dORA d,XASL d,Xora [d],YCLCORA a,YInc AtcsTrb aORA a,XASL a,Xora al,X
2xJSR aAND (d,X)jsl aland d,SBIT dAND dROL dand [d]PLPAND #ROL ApldBIT aAND aROL aand al
3xBMI rAND (d),YAnd (d)and (d,S),YBit d,XAND d,XROL d,Xand [d],YSECAND a,YDec AtscBit a,XAND a,XROL a,Xand al,X
4xRTIEOR (d,X)wdmeor d,Smvp s,dEOR dLSR deor [d]PHAEOR #LSR AphkJMP aEOR aLSR aeor al
5xBVC rEOR (d),YEor (d)eor (d,S),Ymvn s,dEOR d,XLSR d,Xeor [d],YCLIEOR a,YPhytcdjmp alEOR a,XLSR a,Xeor al,X
6xRTSADC (d,X)per rladc d,SStz dADC dROR dadc [d]PLAADC #ROR ArtlJMP (a)ADC aROR aadc al
7xBVS rADC (d),YAdc (d)adc (d,S),YStz d,XADC d,XROR d,Xadc [d],YSEIADC a,YPlytdcJmp (a,X)ADC a,XROR a,Xadc al,X
8xBra rSTA (d,X)brl rlsta d,SSTY dSTA dSTX dsta [d]DEYBit #TXAphbSTY aSTA aSTX asta al
9xBCC rSTA (d),YSta (d)sta (d,S),YSTY d,XSTA d,XSTX d,Ysta [d],YTYASTA a,YTXStxyStz aSTA a,XStz a,Xsta al,X
AxLDY #LDA (d,X)LDX #lda d,SLDY dLDA dLDX dlda [d]TAYLDA #TAXplbLDY aLDA aLDX alda al
BxBCS rLDA (d),YLda (d)lda (d,S),YLDY d,XLDA d,XLDX d,Ylda [d],YCLVLDA a,YTSXtyxLDY a,XLDA a,XLDX a,Ylda al,X
CxCPY #CMP (d,X)rep #cmp d,SCPY dCMP dDEC dcmp [d]INYCMP #DEXwaiCPY aCMP aDEC acmp al
DxBNE rCMP (d),YCmp (d)cmp (d,S),Ypei dCMP d,XDEC d,Xcmp [d],YCLDCMP a,YPhxstpjml (a)CMP a,XDEC a,Xcmp al,X
ExCPX #SBC (d,X)sep #sbc d,SCPX dSBC dINC dsbc [d]INXSBC #NOPxbaCPX aSBC aINC asbc al
FxBEQ rSBC (d),YSbc (d)sbc (d,S),Ypea aSBC d,XINC d,Xsbc [d],YSEDSBC a,YPlxxcejsr (a,X)SBC a,XINC a,Xsbc al,X
+

6502 Instructions

+

+ Most instructions that explicitly reference memory locations have bit + patterns of the form aaabbbcc. The aaa and cc bits + determine the opcode, and the bbb bits determine the addressing mode. +

+

+ Instructions with cc = 01 are the most regular, and are + therefore considered first. The aaa bits determine the opcode + as follows: +

+ + + + + + + + + + +
aaaopcode
000ORA
001AND
010EOR
011ADC
100STA
101LDA
110CMP
111SBC
+

+ And the addressing mode (bbb) bits: +

+ + + + + + + + + + +
bbbaddressing mode
000(zero page,X)
001zero page
010#immediate
011absolute
100(zero page),Y
101zero page,X
110absolute,Y
111absolute,X
+

+ Putting it all together: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 ORAANDEORADCSTALDACMPSBC
(zp,X)0121416181A1C1E1
zp0525456585A5C5E5
#09294969 A9C9E9
abs0D2D4D6D8DADCDED
(zp),Y1131517191B1D1F1
zp,X1535557595B5D5F5
abs,Y1939597999B9D9F9
abs,X1D3D5D7D9DBDDDFD
+

+ The only irregularity is the absence of the nonsensical immediate STA + instruction. +

+

+ Next we consider the cc = 10 instructions. These have a + completely different set of opcodes: +

+ + + + + + + + + + +
aaaopcode
000ASL
001ROL
010LSR
011ROR
100STX
101LDX
110DEC
111INC
+

+ The addressing modes are similar to the 01 case, but not quite + the same: +

+ + + + + + + + +
bbbaddressing mode
000#immediate
001zero page
010accumulator
011absolute
101zero page,X
111absolute,X
+

+ Note that bbb = 100 and 110 are missing. Also, + with STX and LDX, "zero page,X" addressing becomes "zero page,Y", and + with LDX, "absolute,X" becomes "absolute,Y". +

+

+ These fit together like this: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 ASLROLLSRRORSTXLDXDECINC
#     A2  
zp0626466686A6C6E6
A0A2A4A6A    
abs0E2E4E6E8EAECEEE
zp,X/zp,Y1636567696B6D6F6
abs,X/abs,Y1E3E5E7E BEDEFE
+

+ 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. +

+

+ Next, the cc = 00 instructions. Again, the opcodes are + different: +

+ + + + + + + + + +
aaaopcode
001BIT
010JMP
011JMP (abs)
100STY
101LDY
110CPY
111CPX
+

+ It's debatable whether the JMP instructions belong in this + list...I've included them because they do seem to fit, + provided one considers the indirect JMP a separate opcode rather than + a different addressing mode of the absolute JMP. +

+

+ The addressing modes are the same as the 10 case, except that + accumulator mode is missing. +

+ + + + + + + +
bbbaddressing mode
000#immediate
001zero page
011absolute
101zero page,X
111absolute,X
+

+ And here's how they fit together: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 BITJMPJMP()STYLDYCPYCPX
#    A0C0E0
zp24  84A4C4E4
abs2C4C6C8CACCCEC
zp,X   94B4  
abs,X    BC  
+

+ 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 (bbb = + 010) 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". +

+

+ No instructions have the form aaabbb11. +

+

+ The conditional branch instructions all have the form xxy10000. + The flag indicated by xx is compared with y, and the + branch is taken if they are equal. +

+ + + + + + +
xxflag
00negative
01overflow
10carry
11zero
+

+ This gives the following branches: +

+ + + + + +
BPLBMIBVCBVSBCCBCSBNEBEQ
1030507090B0D0F0
+

+ The remaining instructions are probably best considered simply by + listing them. Here are the interrupt and subroutine instructions: +

+ + + +
BRKJSR absRTIRTS
00204060
+

+ (JSR is the only absolute-addressing instruction that doesn't fit the + aaabbbcc pattern.) +

+

+ Other single-byte instructions: +

+ + + + + + + + + + + + + + + + + + + + + +
PHPPLPPHAPLADEYTAYINYINX
0828486888A8C8E8
+ + + + + + + + + + + + + + + + + + + + + +
CLCSECCLISEITYACLVCLDSED
1838587898B8D8F8
+ + + + + + + + + + + + + + + + + +
TXATXSTAXTSXDEXNOP
8A9AAABACAEA
+

Instruction timing

+

+ The time required for 6502 instruction to execute is regular and + predictable. The primary rule is this: +

+
+

+ Each byte read from or written to memory requires one clock cycle. +

+
+

+ 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.) +

+ +

"Undocumented" 6502 instructions

+

+ The above-described instructions (the ones shown in GREEN UPPERCASE 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?" +

+ 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. +

+

+ However, there are some facts that seem to be common across all 6502s. + The most insteresting case is the cc = 11 instructions: + these execute the adjacent cc = 01 and cc = + 10 instructions simultaneously. For example, AF + executes AD ("LDA absolute") and AE ("LDX absolute") at + the same time, putting the same value in both the accumulator and the X + register. +

+

+ In some cases the 01 and 10 instructions are + incompatible. For example, 8F executes 8D ("STA + absolute") and 8E ("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. +

+

+ The behavior of the 11 instructions is especially problematic in + those cases where the adjacent 01 or 10 instruction is also + undocumented. Sometimes you can get a partial idea of what happens by + looking at what the missing 01 or 10 instruction would be + if that opcode/addressing mode combination weren't missing. + Xxxx1011 instructions are also problematic--some of these seem + to mix not only the adjacent 01 and 10 instructions, but + also the immediate mode of the corresponding 10 instruction. +

+

+ Most of the missing 00, 01, and 10 instructions + seem to behave like NOPs, but using the addressing mode indicated by + the bbb bits. But apparently this isn't always reliable--there + are reports of some of these instructions occasionally locking up the + processor. +

+

+ Instructions of the form xxxx0010 usually lock up the processor, + so that a reset is required to recover. The instructions 82, + C2, and E2 (corresponding to the nonexistant immediate + mode of STX, DEC, and INC) may sometimes behave as two-byte NOPs, but don't + count on it. +

+

65C02 Instructions

+

+ 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 cc = 11 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. +

+

+ The new zero-page indirect addressing mode fills the previously-unused + bbb = 100 slot of the cc = 10 instructions, + but the opcodes are those of the cc = 01 instructions. +

+ + + + + + + + + + + + + + + + + + + + + + + +
 ORAANDEORADCSTALDACMPSBC
(zp)1232527292B2D2F2
+

+ "JMP (abs,X)" is right where it ought to be (011 111 00), if one + continues to regard the indirect JMP as a separate opcode from the + absolute JMP: +

+ + + + + + + + + +
 JMP()
abs,X7C
+

+ "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: +

+ + + + + + + + + + + + + + + + + +
 BIT
#89
zp,X34
abs,X3C
+

+ TSB ended up in a reasonable place (000bbb00): +

+ + + + + + + + + + + + + +
 TSB
zp04
abs0C
+

+ 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: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
 TRBSTZ
zp1464
abs1C9C
zp,X 74
abs,X 9E
+

+ That leaves the relative branch instruction +

+ + + + + + + +
BRA
80
+

+ and the single-byte instructions: +

+ + + + + + + + + + + + + + + + + +
INC ADEC APHYPLYPHXPLX
1A3A5A7ADAFA
+

Additional instructions found on some 65C02s

+

+ Actually, I lied when I said above that the designers of the 65C02 + chose to leave the cc = 11 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.) +

+

+ The bit set and clear instructions have the form xyyy0111, + where x is 0 to clear a bit or 1 to set it, and yyy is + which bit at the memory location to set or clear. +

+ + + + + + + + + + + + + + + + + + + + + + + +
 RMB0RMB1RMB2RMB3RMB4RMB5RMB6RMB7
zp0717273747576777
+ + + + + + + + + + + + + + + + + + + + + + + +
 SMB0SMB1SMB2SMB3SMB4SMB5SMB6SMB7
zp8797A7B7C7D7E7F7
+

+ Similarly, the test-and-branch instructions are of the form + xyyy1111, where x is 0 to test whether the bit is 0, or + 1 to test whether it is 1, and yyy is which bit to test. +

+ + + + + + + + + + + + + + + + + + + + + + + +
 BBR0BBR1BBR2BBR3BBR4BBR5BBR6BBR7
zp,rel0F1F2F3F4F5F6F7F
+ + + + + + + + + + + + + + + + + + + + + + + +
 BBS0BBS1BBS2BBS3BBS4BBS5BBS6BBS7
zp,rel8F9FAFBFCFDFEFFF
+

+ Additionally, the WDC version of the 65C02 includes the 65C816's STP and + WAI instructions (see below). +

+

"Undocumented" 65C02 instructions

+

+ There aren't really any undocumented instructions on the 65C02--any + instructions not listed above are documented as performing no + operation. +

+

+ 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. +

+ + + + + + + + +
InstructionBytesCycles
xxxxxx1022
xxxxxx1111
0100010023
x1x1010024
0101110038
11x1110034
+

Actually, it's not quite correct to say that these instructions don't do + anything with their operands. A memory read does 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.

+

Correspondent Jeff Laughton + 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.

+

Instruction timing

+

+ Generally, instruction timing on the 65C02 is the same as on the 6502. + There are, however, a few exceptions: +

+ +

+ Note that BRA is an ordinary branch instruction that always branches. +

+

65C816 Instructions

+

+ The 65C816 uses the cc = 11 instructions, but not for + Rockwell bit-manipulation opcodes. + Most of these are put to work supplying the new long addressing modes + of the 65C816: +

+ + + + + + + + +
bbbaddressing mode
000offset,S
001[direct page]
011absolute long
100(offset,S),Y
101[direct page],Y
111absolute long,X
+

+ These combine with the 01 opcodes: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 ORAANDEORADCSTALDACMPSBC
d,S0323436383A3C3E3
[dp]0727476787A7C7E7
al0F2F4F6F8FAFCFEF
(d,S),Y1333537393B3D3F3
[dp],Y1737577797B7D7F7
al,X1F3F5F7F9FBFDFFF
+

+ The missing 010 and 110 instructions are all single-byte + instructions: +

+ + + + + + + + + + + + + + + + + + + + + +
PHDPLDPHKRTLPHBPLBWAIXBA
0B2B4B6B8BABCBEB
+ + + + + + + + + + + + + + + + + + + + + +
TCSTSCTCDTDCTXYTYXSTPXCE
1B3B5B7B9BBBDBFB
+

+ The remaining instructions are a grab-bag assigned to the few remaining + unused positions: +

+ + + + + + + + + + + + + + + +
COP sig02
JSL al22
WDM42
PER rl62
BRL rl82
REP #C2
SEP #E2
MVP sb,db44
MVN sb,db54
PEI dpD4
PEA absF4
JMP al5C
JML (abs)DC
JSR (abs,X)FC
+

Instruction timing

+

+ 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.)

+

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.

+

Other 65816-specific exceptions:

+ +

LLX > + Neil Parker > + Apple II > 6502 Instruction Set

+

+ Original: July 27, 2004
+ Modified: May 3, 2005
+ Modified: May 29, 2016--Added a new note about 65C02 "undocumented" + opcodes
+ Modified: February 24, 2019--Added information about instruction timing
+ Modified: April 4, 2019--Added missing info about 65618 REP and SEP + timing
+ 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
+

+

Thanks to Jeff Laughton for several helpful comments.

+ + diff --git a/emu/6502.c b/emu/6502.c index 0bb1993..8b24901 100644 --- a/emu/6502.c +++ b/emu/6502.c @@ -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++; } diff --git a/emu/6502.h b/emu/6502.h index 1af25bc..b5ab14a 100644 --- a/emu/6502.h +++ b/emu/6502.h @@ -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 + diff --git a/emu/emu.c b/emu/emu.c index fbd9bb7..7d5c37c 100644 --- a/emu/emu.c +++ b/emu/emu.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 +#include + +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 ); +} diff --git a/emu/emul.h b/emu/emul.h index cefa020..131cd4c 100644 --- a/emu/emul.h +++ b/emu/emul.h @@ -1,9 +1,37 @@ #ifndef EMUL_H #define EMUL_H +#include "6502.h" +#include "memory.h" + +#include + +#ifdef WITH_GUI +#include +#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 -- cgit v1.2.3-54-g00ecf