Skip to content

Commit b2e6f1f

Browse files
committed
Some testing code
1 parent 64e58f7 commit b2e6f1f

File tree

5 files changed

+427
-2
lines changed

5 files changed

+427
-2
lines changed

src/main/java/com/rox/emu/processor/mos6502/Mos6502.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,21 @@ public void step(int steps){
110110
step();
111111
}
112112

113+
public void tick(){
114+
log.debug("TICK >>>");
115+
116+
//TODO are we in the middle of an instruction?
117+
//TODO finish it
118+
//TODO if not, fetch the next instruction
119+
}
120+
113121
/**
114122
* Execute the next program instruction as per {@link Registers#getNextProgramCounter()}
115123
*/
116124
public void step() {
117125
log.debug("STEP >>>");
118126

119-
final Mos6502OpCode opCode = Mos6502OpCode.from(nextProgramByte().getRawValue());
127+
final Mos6502OpCode opCode = Mos6502OpCode.from(nextProgramByte().getRawValue()); //One clock tick
120128

121129
//Execute the opcode
122130
log.debug("Instruction: {}...", opCode.getOpCodeName());

src/main/java/com/rox/emu/processor/mos6502/util/Program.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.rox.emu.processor.mos6502.util;
22

3-
43
import com.rox.emu.UnknownTokenException;
54
import com.rox.emu.env.RoxByte;
65
import com.rox.emu.processor.mos6502.op.Mos6502OpCode;
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
package com.rox.emu.timing;
2+
3+
import com.rox.emu.env.RoxByte;
4+
import com.rox.emu.env.RoxWord;
5+
import com.rox.emu.mem.Memory;
6+
import com.rox.emu.mem.SimpleMemory;
7+
import com.rox.emu.processor.mos6502.util.Mos6502Compiler;
8+
import com.rox.emu.processor.mos6502.util.Program;
9+
import com.rox.emu.timing.tmp.MicroOperation;
10+
import com.rox.emu.timing.tmp.State;
11+
12+
import java.util.*;
13+
14+
import static com.rox.emu.timing.SimpleClock.tmp_AddressingMode.*;
15+
import static com.rox.emu.timing.SimpleClock.MicroOperationType.*;
16+
import static com.rox.emu.timing.SimpleClock.tmp_Operation.LDA;
17+
18+
19+
public class SimpleClock {
20+
private State currentState;
21+
22+
public SimpleClock(final Program program) {
23+
final Memory memory = generateProgramMemory(program);
24+
currentState = new State(256, null, 0, 0, memory, 10, 0,0, new LinkedList<>()); //Start at 256 to skip over any zero page data
25+
}
26+
27+
private Memory generateProgramMemory(final Program program){
28+
final RoxByte[] programAsByteArray = program.getProgramAsByteArray();
29+
//XXX: Why did this stop working?
30+
//System.out.println("Program: " + Arrays.stream(programAsByteArray).map(RoxByte::getRawValue).collect(Collectors.toList()));
31+
32+
//Set up some simple memory with only the program in it
33+
final SimpleMemory memory = new SimpleMemory(programAsByteArray.length + 256); //+256 to allow for zero page addressing
34+
/*TEST*/memory.setByteAt(RoxWord.fromLiteral(177), RoxByte.fromLiteral(42));
35+
36+
memory.setBlock(RoxWord.fromLiteral(256), programAsByteArray);
37+
return memory;
38+
}
39+
40+
private enum MemoryMode { READ, WRITE}
41+
42+
// private void setAddressBus(final int ADH, final int ADL, final MemoryMode mode) {
43+
// this.addressBus = 16 * ADH + ADL;
44+
// if (mode == MemoryMode.WRITE) {
45+
// System.out.println("Setting address bus to " + addressBus + " for WRITE operation");
46+
// //TODO you need a source to set the data bus to
47+
// } else {
48+
// System.out.println("Setting address bus to " + addressBus + " for READ operation");
49+
// //TODO you need a destination to write the data bus to
50+
// }
51+
// }
52+
53+
//XXX This name indicates bad design
54+
private State fetchAndDecode(final State currentState) {
55+
State tmpState = currentState;
56+
if (currentState.getMicroOperations().isEmpty()){ //No opcode current being processed
57+
//Fetch opcode from memory & decode
58+
final TMP_OpCode nextOpCode = TMP_OpCode.getByValue(currentState.getByteInMemory().getRawValue());
59+
tmpState = currentState.withOpcode(nextOpCode).withMicroOperations(new LinkedList<>(nextOpCode.getSteps()));
60+
/*DEBUG*/System.out.println(" [PC:" + currentState.getPc() + "] Loaded opcode: " + tmpState.getLoadedOpCode() + " (Operation: " + tmpState.getLoadedOpCode().operation + ", Addressing Mode: " + tmpState.getLoadedOpCode().addressingMode + ")");
61+
}
62+
63+
return tmpState;
64+
}
65+
66+
private void clockTick() {
67+
// Simulate a clock tick
68+
System.out.println("[TICK]");
69+
70+
currentState = fetchAndDecode(currentState);
71+
final MicroOperationType microOp = currentState.getMicroOperations().poll();
72+
final MicroOperation executableMicroOp = microOp.getOperation();
73+
/*DEBUG*/System.out.println("Executing '" + microOp + "'");
74+
currentState = executableMicroOp.execute(currentState);
75+
}
76+
77+
//TODO should use address bus and data bus instead of operands to store data
78+
public enum MicroOperationType {
79+
FETCH_OPCODE((originalState) -> originalState
80+
.withOpcode(TMP_OpCode.getByValue(originalState.getByteInMemory().getRawValue()))
81+
.withIncrementedPc()
82+
),
83+
FETCH_OPERAND_1((originalState) -> originalState
84+
.withOperand1(originalState.getByteInMemory().getRawValue())
85+
.withIncrementedPc()
86+
),
87+
FETCH_OPERAND_2((originalState) -> originalState
88+
.withOperand2(originalState.getByteInMemory().getRawValue())
89+
.withIncrementedPc()
90+
),
91+
//This is equivalence to read zero page address?
92+
TEST((s) -> s.addressing(s.getLoadedOperand1()).postRead().withIncrementedPc()),
93+
READ_ZERO_PAGE_ADDRESS((originalState) -> originalState
94+
//TODO put address (from s.getLoadedOperand1()) on the address bus
95+
//TODO fetch the operand from the data bus
96+
//Get the value at zero page provided in s.getLoadedOperand1(),
97+
.withData(originalState.getMemory().getByte(RoxWord.from(RoxByte.fromLiteral(originalState.getLoadedOperand1()))).getAsInt())
98+
.withIncrementedPc()),
99+
ADD_Y_REGISTER((origingalState) -> origingalState
100+
//Add Y register to operand
101+
.withData(origingalState.getYRegister() + origingalState.getLoadedOperand1())
102+
.withIncrementedPc()
103+
),
104+
LOAD_ACCUMULATOR((s) -> s
105+
.withAccumulator(s.getLoadedOperand1())
106+
.withIncrementedPc()
107+
);
108+
109+
private final MicroOperation operation;
110+
111+
public MicroOperation getOperation() {
112+
return operation;
113+
}
114+
115+
MicroOperationType(final MicroOperation o) {
116+
this.operation = o;
117+
}
118+
}
119+
120+
/**
121+
* LDA Indexed (Y) Addressing Mode
122+
* LDA ($00),Y
123+
*
124+
* Cycle 1: Fetch opcode
125+
* Cycle 2: Fetch operand
126+
* Cycle 3: Read base address
127+
* Cycle 4: Add Y register
128+
* Cycle 5: Fetch final value
129+
*/
130+
131+
int valueAtZeroPAage(final int address) {
132+
return 10; //Placeholder
133+
}
134+
135+
public enum TMP_OpCode {
136+
LDA_Z(0xA5, LDA, ZERO_PAGE, new MicroOperationType[]{}),
137+
LDA_I(0xA9, LDA, IMMEDIATE, new MicroOperationType[]{}),
138+
LDA_A(0xAD, LDA, ABSOLUTE, new MicroOperationType[]{}),
139+
LDA_Z_IX(0xB5, LDA, ZERO_PAGE_INDEXED_X, new MicroOperationType[]{}),
140+
LDA_AX(0xBD, LDA, ABSOLUTE_INDEXED_X, new MicroOperationType[]{}),
141+
LDA_IX(0xA1, LDA, INDIRECT_INDEXED_X, new MicroOperationType[]{}),
142+
LDA_AY(0xB9, LDA, ABSOLUTE_INDEXED_Y, new MicroOperationType[]{}),
143+
LDA_IY(0xB1, LDA, INDIRECT_INDEXED_Y, new MicroOperationType[]{
144+
//Automatic micro operation
145+
//FETCH_OPCODE,
146+
//Microoperations from Addressing Mode
147+
// FETCH_OPERAND_1,
148+
// READ_ZERO_PAGE_ADDRESS, //XXX Does having no argument here not make it a general case solution?
149+
// ADD_Y_REGISTER,
150+
LOAD_ACCUMULATOR
151+
});
152+
153+
private final int opcode;
154+
private final tmp_Operation operation;
155+
private final tmp_AddressingMode addressingMode;
156+
private final List<MicroOperationType> opCodeSteps;
157+
158+
public List<MicroOperationType> getSteps() {
159+
final List<MicroOperationType> allSteps = new ArrayList<>(addressingMode.getSteps());
160+
allSteps.addAll(opCodeSteps);
161+
return Collections.unmodifiableList(allSteps);
162+
}
163+
164+
public static TMP_OpCode getByValue(final int opecode_id) throws RuntimeException {
165+
return Arrays.stream(TMP_OpCode.values())
166+
.filter(opCode -> opCode.opcode == opecode_id)
167+
.findFirst()
168+
.orElseThrow(() -> new RuntimeException("Unknown opcode: " + opecode_id)); //TODO Use custom exception
169+
}
170+
171+
TMP_OpCode(final int opcode,
172+
final tmp_Operation operation,
173+
final tmp_AddressingMode addressingMode,
174+
final MicroOperationType[] steps) {
175+
this.opcode = opcode;
176+
this.operation = operation;
177+
this.addressingMode = addressingMode;
178+
this.opCodeSteps = Arrays.asList(steps);
179+
}
180+
181+
@Override
182+
public String toString() {
183+
return this.name();
184+
}
185+
}
186+
187+
public enum tmp_Operation {
188+
/**
189+
* r.setFlagsBasedOn(v);
190+
* r.setRegister(ACCUMULATOR, v);
191+
*/
192+
LDA // Load Accumulator
193+
}
194+
195+
public enum tmp_AddressingMode {
196+
IMMEDIATE(new MicroOperationType[]{}),
197+
ABSOLUTE(new MicroOperationType[]{}),
198+
ZERO_PAGE(new MicroOperationType[]{}),
199+
ZERO_PAGE_INDEXED_X(new MicroOperationType[]{}),
200+
ZERO_PAGE_INDEXED_Y(new MicroOperationType[]{}),
201+
INDIRECT_INDEXED_X(new MicroOperationType[]{}),
202+
/** <i>Indexed indirect</i>: Expects a one byte argument and an offset in the X Register added together they
203+
* give an address in Zero Page that itself contains a two byte address to be used in the operation */
204+
INDIRECT_INDEXED_Y(new MicroOperationType[]{
205+
FETCH_OPERAND_1,
206+
READ_ZERO_PAGE_ADDRESS, //XXX Does having no argument here not make it a general case solution?
207+
ADD_Y_REGISTER
208+
}),
209+
ABSOLUTE_INDEXED_X(new MicroOperationType[]{}),
210+
ABSOLUTE_INDEXED_Y(new MicroOperationType[]{});
211+
212+
private final List<MicroOperationType> steps;
213+
214+
public List<MicroOperationType> getSteps() {
215+
return Collections.unmodifiableList(steps);
216+
}
217+
218+
tmp_AddressingMode(final MicroOperationType[] steps) {
219+
this.steps = Arrays.asList(steps);
220+
}
221+
}
222+
223+
public static void main(String[] args) {
224+
final String code = "LDA ($01),Y";
225+
System.out.println("Code: \"" + code + "\"");
226+
final Mos6502Compiler compiler = new Mos6502Compiler(code);
227+
final Program program = compiler.compileProgram();
228+
System.out.println("Program compiled successfully: " + program.getLength() + " bytes.");
229+
230+
final SimpleClock clock = new SimpleClock(program);
231+
232+
System.out.println(clock.currentState);
233+
for (int i=0; i<4; i++){
234+
clock.clockTick();
235+
System.out.println(clock.currentState);
236+
}
237+
238+
// while (i++ < 20) {
239+
// clock.clockTick();
240+
// try {
241+
// /* ~1ms delay (1000Hz - simulating slower clock)
242+
// * This isn't ideal as 1ms in Java time is not guaranteed to be exactly 1ms,
243+
// */
244+
// Thread.sleep(1);
245+
// } catch (InterruptedException e) {
246+
// break;
247+
// }
248+
// }
249+
}
250+
}
251+
252+
253+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.rox.emu.timing.tmp;
2+
3+
import com.rox.emu.processor.mos6502.util.Program;
4+
5+
@FunctionalInterface
6+
public interface MicroOperation {
7+
State execute(final State inputState);
8+
}

0 commit comments

Comments
 (0)