Skip to content

Commit 6755f88

Browse files
committed
1.1
- Vastly improved clock speed (by about 1000x!) - Added displays for total clocks, cycles, controls, version, and clock speed - GUI now updates at 60fps - RAM and ROM string routines no longer run every frame - added a slower clock mode
1 parent 1eeab97 commit 6755f88

File tree

6 files changed

+102
-57
lines changed

6 files changed

+102
-57
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Feel free to fork it, improve it, whatever, just link back to here. Enjoy!
2121
- H/J - Decrement/Increment RAM Page
2222
- K/L - Decrement/Increment ROM Page
2323
- R - Reset
24+
- S - Toggle Slow Clock
2425

2526
You can load ```.bin``` files into RAM or ROM using the File Pickers in the top right. It should be fully compatible with any binary compiled for the 6502 kit, except if it uses any unimplemented features. I might get to these sometime in the future. If I do, the repo will be updated.
2627

src/CPU.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import java.util.Arrays;
22

33
public class CPU {
4-
public String[] opcodes;
5-
64
public byte flags = 0x00;
75
//C,Z,I,D,B,U,V,N
86
//Carry, Zero, Interrupt Disable, Decimal, Break, Unused, Overflow, Negative
@@ -18,7 +16,10 @@ public class CPU {
1816
public byte opcode = 0x00;
1917
public int cycles = 0;
2018

21-
int additionalCycles = 0;
19+
public double ClocksPerSecond = 0;
20+
public long startTime = 0;
21+
22+
public int additionalCycles = 0;
2223

2324
public Instruction[] lookup = new Instruction[0xFF];
2425

@@ -312,6 +313,10 @@ void clock() {
312313
this.getClass().getMethod(lookup[Byte.toUnsignedInt(opcode)].opcode).invoke(this);
313314
} catch (Exception e) {e.printStackTrace();}
314315
}
316+
317+
if (((System.currentTimeMillis()-startTime)/1000) > 0)
318+
ClocksPerSecond = EaterEmulator.clocks/((System.currentTimeMillis()-startTime)/1000);
319+
315320
EaterEmulator.clocks++;
316321
cycles--;
317322
}
@@ -331,12 +336,15 @@ void reset() {
331336
programCounter = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi));
332337

333338
EaterEmulator.clocks = 0;
339+
ClocksPerSecond = 0;
334340

335341
addressRelative = 0;
336342
addressAbsolute = 0;
337343
fetched = 0;
338344

339345
cycles = 8;
346+
347+
startTime = System.currentTimeMillis();
340348
}
341349

342350
void irq() {

src/DisplayPanel.java

+52-43
Original file line numberDiff line numberDiff line change
@@ -3,85 +3,100 @@
33
import javax.swing.JPanel;
44
import javax.swing.Timer;
55

6-
public class DisplayPanel extends JPanel implements ActionListener, KeyListener, MouseMotionListener {
6+
public class DisplayPanel extends JPanel implements ActionListener, KeyListener {
77
Timer t;
8-
Point mousePos = new Point(0,0);
98
int ramPage = 0;
109
int romPage = 0;
1110

11+
String ramPageString = "";
12+
String romPageString = "";
13+
1214
public DisplayPanel() {
1315
super(null);
1416

15-
t = new javax.swing.Timer(100, this);
17+
t = new javax.swing.Timer(16, this);
1618
t.start();
1719
setBackground(Color.blue);
1820
setPreferredSize(new Dimension(1936, 966));
1921

22+
romPageString = EaterEmulator.rom.ROMString.substring(romPage*960,(romPage+1)*960);
23+
ramPageString = EaterEmulator.ram.RAMString.substring(ramPage*960,(ramPage+1)*960);
24+
2025
this.setFocusable(true);
2126
this.requestFocus();
2227
this.addKeyListener(this);
23-
this.addMouseMotionListener(this);
2428
}
2529

2630
public void paintComponent(Graphics g) {
2731
super.paintComponent(g);
28-
29-
g.setColor(Color.white);
32+
g.setColor(Color.white);
33+
//g.drawString("Render Mode: paintComponent",5,15);
34+
35+
// g.setColor(getBackground());
36+
// g.fillRect(0, 0, EaterEmulator.getWindows()[1].getWidth(), EaterEmulator.getWindows()[1].getHeight());
37+
// g.setColor(Color.white);
38+
// g.drawString("Render Mode: fillRect",5,15);
3039

3140
//Title
3241
g.setFont(new Font("Calibri Bold", 50, 50));
3342
g.drawString("Ben Eater 6502 Emulator", 40, 50);
3443

35-
//Clocks
44+
//Version
3645
g.setFont(new Font("Courier New Bold",20,20));
37-
g.drawString("Clocks: "+EaterEmulator.clocks, 40, 80);
46+
g.drawString("v"+EaterEmulator.versionString, 7, 1033);
3847

39-
//Mouse Position
40-
// g.setFont(new Font("Arial",10,10));
41-
// g.drawString(mousePos.toString(), 10, 10);
48+
//Clocks
49+
g.drawString("Clocks: "+EaterEmulator.clocks, 40, 80);
50+
g.drawString("Speed: "+EaterEmulator.cpu.ClocksPerSecond+" Hz"+(EaterEmulator.slowerClock ? " (Slow)" : ""), 40, 110);
4251

4352
//PAGE INDICATORS
44-
g.setFont(new Font("Courier New Bold",20,20));
4553
g.drawString("(K) <-- "+ROMLoader.byteToHexString((byte)(romPage+0x80))+" --> (L)", 1600, 950);
4654
g.drawString("(H) <-- "+ROMLoader.byteToHexString((byte)ramPage)+" --> (J)", 1200, 950);
4755

4856
//ROM
4957
g.drawString("ROM", 1690, 130);
50-
drawString(g,EaterEmulator.rom.toStringWithOffset(8,0x8000,true).substring(romPage*960,(romPage+1)*960), 1525, 150);
58+
drawString(g,romPageString, 1525, 150);
5159

5260
//RAM
5361
g.drawString("RAM", 1280, 130);
54-
drawString(g,EaterEmulator.ram.toString(8,true).substring(ramPage*960,(ramPage+1)*960), 1125, 150);
62+
drawString(g,ramPageString, 1125, 150);
5563

5664
//CPU
57-
g.drawString("CPU Registers:",50,120);
58-
g.drawString("A: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.a)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.a)+")", 35, 150);
59-
g.drawString("X: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.x)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.x)+")", 35, 180);
60-
g.drawString("Y: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.y)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.y)+")", 35, 210);
61-
g.drawString("Stack Pointer: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.stackPointer)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.stackPointer)+")", 35, 240);
62-
g.drawString("Program Counter: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Short.toUnsignedInt(EaterEmulator.cpu.programCounter)), 16)+" ("+ROMLoader.padStringWithZeroes(Integer.toHexString(Short.toUnsignedInt(EaterEmulator.cpu.programCounter)),4)+")", 35, 270);
63-
g.drawString("Flags: ", 35, 300);
65+
g.drawString("CPU Registers:",50,140);
66+
g.drawString("A: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.a)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.a)+")", 35, 170);
67+
g.drawString("X: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.x)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.x)+")", 35, 200);
68+
g.drawString("Y: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.y)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.y)+")", 35, 230);
69+
g.drawString("Stack Pointer: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.stackPointer)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.stackPointer)+")", 35, 260);
70+
g.drawString("Program Counter: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Short.toUnsignedInt(EaterEmulator.cpu.programCounter)), 16)+" ("+ROMLoader.padStringWithZeroes(Integer.toHexString(Short.toUnsignedInt(EaterEmulator.cpu.programCounter)),4)+")", 35, 290);
71+
g.drawString("Flags: ", 35, 320);
6472

6573
g.drawString("Absolute Address: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Short.toUnsignedInt(EaterEmulator.cpu.addressAbsolute)), 16)+" ("+ROMLoader.byteToHexString((byte)(EaterEmulator.cpu.addressAbsolute/0xFF))+ROMLoader.byteToHexString((byte)EaterEmulator.cpu.addressAbsolute)+")", 35, 350);
6674
g.drawString("Relative Address: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Short.toUnsignedInt(EaterEmulator.cpu.addressRelative)), 16)+" ("+ROMLoader.byteToHexString((byte)(EaterEmulator.cpu.addressRelative/0xFF))+ROMLoader.byteToHexString((byte)EaterEmulator.cpu.addressRelative)+")", 35, 380);
6775
g.drawString("Opcode: "+EaterEmulator.cpu.lookup[Byte.toUnsignedInt(EaterEmulator.cpu.opcode)]+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.opcode)+")", 35, 410);
76+
g.drawString("Cycles: "+EaterEmulator.cpu.cycles, 35, 440);
6877

6978
int counter = 0;
7079
String flagsString = "CZIDBUVN";
7180
for (char c : ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.flags)),8).toCharArray()) {
7281
g.setColor((c == '1') ? Color.green : Color.red);
73-
g.drawString(String.valueOf(flagsString.charAt(counter)), 120+15*counter, 300);
82+
g.drawString(String.valueOf(flagsString.charAt(counter)), 120+15*counter, 320);
7483
counter++;
7584
}
7685

7786
g.setColor(Color.white);
7887
//VIA
79-
g.drawString("VIA Registers:",50,480);
80-
g.drawString("PORT A: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.PORTA)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.PORTA)+")", 35, 510);
81-
g.drawString("PORT B: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.PORTB)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.PORTB)+")", 35, 540);
82-
g.drawString("DDR A: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.DDRA)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.DDRA)+")", 35, 570);
83-
g.drawString("DDR B: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.DDRB)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.DDRB)+")", 35, 600);
88+
g.drawString("VIA Registers:",50,490);
89+
g.drawString("PORT A: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.PORTA)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.PORTA)+")", 35, 520);
90+
g.drawString("PORT B: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.PORTB)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.PORTB)+")", 35, 550);
91+
g.drawString("DDR A: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.DDRA)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.DDRA)+")", 35, 580);
92+
g.drawString("DDR B: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.DDRB)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.DDRB)+")", 35, 610);
8493

94+
//Controls
95+
g.drawString("Controls:", 50, 750);
96+
g.drawString("C - Toggle Clock", 35, 780);
97+
g.drawString("Space - Pulse Clock", 35, 810);
98+
g.drawString("R - Reset", 35, 840);
99+
g.drawString("S - Toggle Slower Clock", 35, 870);
85100
}
86101

87102
public static void drawString(Graphics g, String text, int x, int y) {
@@ -92,6 +107,7 @@ public static void drawString(Graphics g, String text, int x, int y) {
92107
@Override
93108
public void actionPerformed(ActionEvent e) {
94109
if (e.getSource().equals(t)) {
110+
ramPageString = EaterEmulator.ram.RAMString.substring(ramPage*960,(ramPage+1)*960);
95111
this.repaint();
96112
}
97113
}
@@ -112,50 +128,43 @@ public void keyTyped(KeyEvent arg0) {
112128
case 'l':
113129
if (romPage < 0x80) {
114130
romPage+=1;
131+
romPageString = EaterEmulator.rom.ROMString.substring(romPage*960,(romPage+1)*960);
115132
}
116133
break;
117134
case 'k':
118135
if (romPage > 0) {
119136
romPage-=1;
137+
romPageString = EaterEmulator.rom.ROMString.substring(romPage*960,(romPage+1)*960);
120138
}
121139
break;
122140
case 'j':
123141
if (ramPage < 0x80) {
124142
ramPage+=1;
143+
ramPageString = EaterEmulator.ram.RAMString.substring(ramPage*960,(ramPage+1)*960);
125144
}
126145
break;
127146
case 'h':
128147
if (ramPage > 0) {
129148
ramPage-=1;
149+
ramPageString = EaterEmulator.ram.RAMString.substring(ramPage*960,(ramPage+1)*960);
130150
}
131151
break;
132152
case 'r':
133153
EaterEmulator.cpu.reset();
134154
EaterEmulator.lcd.reset();
135155
EaterEmulator.via = new VIA();
136156
EaterEmulator.ram = new RAM();
157+
ramPageString = EaterEmulator.ram.RAMString.substring(ramPage*960,(ramPage+1)*960);
137158
break;
138159
case ' ':
139160
EaterEmulator.cpu.clock();
140161
break;
141162
case 'c':
142-
EaterEmulator.haltFlag = !EaterEmulator.haltFlag;
163+
EaterEmulator.clockState = !EaterEmulator.clockState;
164+
break;
165+
case 's':
166+
EaterEmulator.slowerClock = !EaterEmulator.slowerClock;
143167
break;
144168
}
145-
this.repaint();
146-
}
147-
148-
@Override
149-
public void mouseDragged(MouseEvent arg0) {
150-
151169
}
152-
153-
@Override
154-
public void mouseMoved(MouseEvent arg0) {
155-
mousePos.setX(arg0.getX());
156-
mousePos.setY(arg0.getY());
157-
158-
this.repaint();
159-
}
160-
161170
}

src/EaterEmulator.java

+29-11
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@
88
import javax.swing.*;
99

1010
public class EaterEmulator extends JFrame implements ActionListener {
11+
public static EaterEmulator emu;
12+
public static String versionString = "1.1";
13+
1114
//Swing Things
1215
JPanel p = new JPanel();
1316
JPanel header = new JPanel();
1417
JFileChooser fc = new JFileChooser();
1518
JButton ROMopenButton = new JButton("Open ROM File");
1619
JButton RAMopenButton = new JButton("Open RAM File");
1720

18-
public DisplayPanel GraphicsPanel = new DisplayPanel();
19-
20-
public Timer clock;
21+
//Clock Stuff
22+
public static Thread clockThread;
23+
public static boolean clockState = false;
2124
public static int clocks = 0;
2225
public static boolean haltFlag = true;
26+
public static boolean slowerClock = false;
2327

2428
//Emulator Things
2529
public static RAM ram = new RAM();
@@ -29,11 +33,12 @@ public class EaterEmulator extends JFrame implements ActionListener {
2933
public static Bus bus = new Bus();
3034
public static CPU cpu = new CPU();
3135

36+
public DisplayPanel GraphicsPanel = new DisplayPanel();
37+
3238
public EaterEmulator() {
3339
//Swing Stuff:
40+
System.setProperty("sun.java2d.opengl", "true");
3441
this.setSize(1920,1080);
35-
clock = new Timer(1,this);
36-
clock.start();
3742

3843
//Open .bin file button
3944
ROMopenButton.setVisible(true);
@@ -52,6 +57,21 @@ public EaterEmulator() {
5257
fc.setVisible(true);
5358
fc.setCurrentDirectory(new File(System.getProperty("user.home") + System.getProperty("file.separator")+ "Downloads"));
5459

60+
//Clock thread setup
61+
clockThread = new Thread(() -> {
62+
while (true) {
63+
if (EaterEmulator.clockState)
64+
cpu.clock();
65+
System.out.print("");
66+
if (slowerClock) {
67+
try {
68+
Thread.sleep(1);
69+
} catch (InterruptedException e) {e.printStackTrace();}
70+
}
71+
}
72+
});
73+
clockThread.start();
74+
5575
//Final Setup
5676
GraphicsPanel.setVisible(true);
5777
this.setTitle("6502 Emulator");
@@ -70,6 +90,7 @@ public void actionPerformed(ActionEvent e) {
7090
rom.setROMArray(ROMLoader.readROM(fc.getSelectedFile()));
7191
}
7292
GraphicsPanel.requestFocus();
93+
GraphicsPanel.romPageString = EaterEmulator.rom.ROMString.substring(GraphicsPanel.romPage*960,(GraphicsPanel.romPage+1)*960);
7394
cpu.reset();
7495
} else if (e.getSource().equals(RAMopenButton)) {
7596
fc.setSelectedFile(new File(""));
@@ -79,15 +100,12 @@ public void actionPerformed(ActionEvent e) {
79100
ram.setRAMArray(ROMLoader.readROM(fc.getSelectedFile()));
80101
}
81102
GraphicsPanel.requestFocus();
82-
cpu.reset();
83-
} else if (e.getSource().equals(clock)) {
84-
if (!haltFlag)
85-
cpu.clock();
103+
GraphicsPanel.ramPageString = EaterEmulator.ram.RAMString.substring(GraphicsPanel.ramPage*960,(GraphicsPanel.ramPage+1)*960);
104+
cpu.reset();
86105
}
87106
}
88107

89108
public static void main(String[] args) {
90-
@SuppressWarnings("unused")
91-
EaterEmulator emu = new EaterEmulator();
109+
emu = new EaterEmulator();
92110
}
93111
}

src/RAM.java

+5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
public class RAM {
22
private byte[] array;
3+
public String RAMString = "";
34

45
public RAM() {
56
array = new byte[0x8000];
67
for (int i = 0; i<0x8000; i++) {
78
array[i] = (byte)0x00;
89
}
10+
RAMString = this.toString(8, true);
911
}
1012

1113
public RAM(byte[] theArray) {
1214
array = theArray;
15+
RAMString = this.toString(8, true);
1316
}
1417

1518
public byte[] getRAMArray() {
@@ -18,6 +21,7 @@ public byte[] getRAMArray() {
1821

1922
public void setRAMArray(byte[] array) {
2023
this.array = array;
24+
RAMString = this.toString(8, true);
2125
}
2226

2327
public byte read(short address) {
@@ -26,6 +30,7 @@ public byte read(short address) {
2630

2731
public void write(short address, byte data) {
2832
array[Short.toUnsignedInt(address)] = data;
33+
RAMString = this.toString(8, true);
2934
}
3035

3136
public String toString(int bytesPerLine, boolean addresses) {

0 commit comments

Comments
 (0)