I recently started new project - a CPU emulator in Java. Base code is written, now I can start to slowly implement new things, but I want code review to be done, to reformat code before it messes up. Unfortunately, I didn't add any Javadocs and didn't do any deeper tests, except checking if basic OP codes are working.
https://github.com/jakub-gonet/Emulated-CPU
Code structure is like so:
- CommandHelper.py - script used to convert Op code to binary form and vice versa
- Main - entry point
- cpu/OPCODE - list of OP codes and what they should do
- cpu/ADDR_TYPE - addressing modes
- cpu/StatusFlags - CPU flags (carry, negative, zero flag etc)
- cpu/programCounter - program counter
- cpu/CPUEmulator - main class to bind rest to it
- cpu/instructionArgument, InstructionMnemonic, Instruction - used to encode a single instruction
- cpu/instruction/InstructionRunner - fetch and run Instruction
- cpu/instruction/converter - package used to convert from bits to OP code and address modes
- cpu/memory - package used to represent CPU memory
(bolded are important)
package cpu.instruction;
import cpu.ADDR_TYPE;
import cpu.instruction.converter.ValueToAddressingMode;
import cpu.instruction.converter.ValueToOPCode;
import cpu.memory.Memory;
public class InstructionRunner {
public static void run(Instruction instruction) {
if (instruction != null) {
Integer arg2Value = (instruction.getArg2() != null) ? instruction.getArg2()
.getValue() : null;
Integer arg1Value = (instruction.getArg1() != null) ? instruction.getArg1()
.getValue() : null;
Integer address = (instruction.getArg1() != null) ? instruction.getArg1()
.getAddress() : null;
Integer value = instruction.getMnemonic()
.getOpcode()
.apply(arg1Value,
arg2Value);
if (instruction.getArgumentCount() > 0) instruction.getArg1()
.getMemory()
.write(address, value);
} else throw new
IllegalArgumentException("ERROR: Instruction can't be null.");
}
public static Instruction fetchInstruction(int address) {
try {
int opCodeAndAddresses = readFromMemory(address);
InstructionMnemonic mnemonic = new InstructionMnemonic(
ValueToOPCode.getOPCode(opCodeAndAddresses)
);
InstructionArgument[] arguments = createArguments(mnemonic.getRequiredArgs(), address, opCodeAndAddresses);
return new Instruction(mnemonic, arguments[0], arguments[1]);
} catch (IndexOutOfBoundsException e) {
System.out.println("ERROR: OP code not recognized.");
} catch (NullPointerException e) {
System.out.println("ERROR: Bad format of instruction.");
}
return null;
}
private static InstructionArgument[] createArguments(int argumentCount, int address, int argumentsCode) {
InstructionArgument arg2 = null;
InstructionArgument arg1 = null;
switch (argumentCount) {
case 2:
int arg2Code = readFromMemory(address + 2);
ADDR_TYPE secondAddressType = getAddressType(2, argumentsCode);
arg2 = new InstructionArgument(arg2Code, secondAddressType);
case 1:
int arg1Code = readFromMemory(address + 1);
ADDR_TYPE firstAddressType = getAddressType(1, argumentsCode);
arg1 = new InstructionArgument(arg1Code, firstAddressType);
}
return new InstructionArgument[] {arg1, arg2};
}
private static ADDR_TYPE getAddressType(int argumentNumber, int operatorAndAddressingModeCode) {
if (argumentNumber == 1) return ValueToAddressingMode.getFirstArgAddressingMode(operatorAndAddressingModeCode);
if (argumentNumber == 2) return ValueToAddressingMode.getSecondArgAddressingMode(operatorAndAddressingModeCode);
throw new IllegalArgumentException("ERROR: OP code argument does not exist.");
}
private static int readFromMemory(int address) {
return Memory.getInstance()
.read(address);
}
}
I mostly wonder if that null policy is good practice and how to manage exceptions in that type of project.